001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/csct/resources/OpenGIS.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.resources; 052 053 // OpenGIS dependencies (SEAGIS) 054 import java.awt.geom.Point2D; 055 import java.awt.geom.Rectangle2D; 056 057 import org.deegree.model.csct.cs.AxisInfo; 058 import org.deegree.model.csct.cs.AxisOrientation; 059 import org.deegree.model.csct.cs.CompoundCoordinateSystem; 060 import org.deegree.model.csct.cs.CoordinateSystem; 061 import org.deegree.model.csct.cs.GeographicCoordinateSystem; 062 import org.deegree.model.csct.cs.HorizontalCoordinateSystem; 063 import org.deegree.model.csct.cs.HorizontalDatum; 064 import org.deegree.model.csct.cs.TemporalCoordinateSystem; 065 import org.deegree.model.csct.cs.TemporalDatum; 066 import org.deegree.model.csct.cs.VerticalCoordinateSystem; 067 import org.deegree.model.csct.cs.VerticalDatum; 068 import org.deegree.model.csct.ct.CoordinateTransformation; 069 import org.deegree.model.csct.ct.CoordinateTransformationFactory; 070 import org.deegree.model.csct.ct.MathTransform; 071 import org.deegree.model.csct.ct.MathTransform2D; 072 import org.deegree.model.csct.ct.TransformException; 073 import org.deegree.model.csct.pt.AngleFormat; 074 import org.deegree.model.csct.pt.CoordinatePoint; 075 import org.deegree.model.csct.pt.Envelope; 076 import org.deegree.model.csct.pt.Latitude; 077 import org.deegree.model.csct.pt.Longitude; 078 import org.deegree.model.csct.pt.MismatchedDimensionException; 079 import org.deegree.model.csct.resources.css.ResourceKeys; 080 import org.deegree.model.csct.resources.css.Resources; 081 082 /** 083 * A set of static methods working on OpenGIS objects. Some of those methods 084 * are useful, but not really rigorous. This is why they do not appear in the 085 * "official" package, but instead in this private one. <strong>Do not rely on 086 * this API!</strong> It may change in incompatible way in any future version. 087 * 088 * @version 1.0 089 * @author Martin Desruisseaux 090 */ 091 public final class OpenGIS { 092 /** 093 * Do not allow creation of 094 * instances of this class. 095 */ 096 private OpenGIS() { 097 } 098 099 /** 100 * Returns the dimension of the first axis of a particular type. 101 * For example, <code>getDimensionOf(cs, AxisInfo.TIME)</code> 102 * would returns the dimension number of time axis. 103 */ 104 public static int getDimensionOf( final CoordinateSystem cs, final AxisInfo axis ) { 105 final int dimension = cs.getDimension(); 106 final AxisOrientation orientation = axis.orientation.absolute(); 107 for ( int i = 0; i < dimension; i++ ) 108 if ( orientation.equals( cs.getAxis( i ).orientation.absolute() ) ) 109 return i; 110 return -1; 111 } 112 113 /** 114 * Returns a two-dimensional coordinate system representing the two first dimensions 115 * of the specified coordinate system. If <code>cs</code> is already a two-dimensional 116 * coordinate system, then it is returned unchanged. Otherwise, if it is a 117 * {@link CompoundCoordinateSystem}, then the head coordinate system is examined. 118 * 119 * @param cs The coordinate system. 120 * @return A two-dimensional coordinate system that represents the two first 121 * dimensions of <code>cs</code>. 122 * @throws IllegalArgumentException if <code>cs</code> can't be reduced to 123 * a two-coordinate system. 124 */ 125 public static CoordinateSystem getCoordinateSystem2D( CoordinateSystem cs ) 126 throws IllegalArgumentException { 127 if ( cs != null ) { 128 while ( cs.getDimension() != 2 ) { 129 if ( !( cs instanceof CompoundCoordinateSystem ) ) { 130 throw new IllegalArgumentException( 131 Resources.format( 132 ResourceKeys.ERROR_CANT_REDUCE_TO_TWO_DIMENSIONS_$1, 133 cs.getName() ) ); 134 } 135 cs = ( (CompoundCoordinateSystem) cs ).getHeadCS(); 136 } 137 } 138 return cs; 139 } 140 141 /** 142 * Returns the first horizontal datum found in a coordinate system, 143 * or <code>null</code> if there is none. Note: in a future version, 144 * we may implement this method directly into {@link CoordinateSystem} 145 * (not sure yet if it would be a good idea). 146 */ 147 public static HorizontalDatum getHorizontalDatum( final CoordinateSystem cs ) { 148 if ( cs instanceof HorizontalCoordinateSystem ) { 149 return ( (HorizontalCoordinateSystem) cs ).getHorizontalDatum(); 150 } 151 if ( cs instanceof CompoundCoordinateSystem ) { 152 HorizontalDatum datum; 153 final CompoundCoordinateSystem comp = (CompoundCoordinateSystem) cs; 154 if ( ( datum = getHorizontalDatum( comp.getHeadCS() ) ) != null ) 155 return datum; 156 if ( ( datum = getHorizontalDatum( comp.getTailCS() ) ) != null ) 157 return datum; 158 } 159 return null; 160 } 161 162 /** 163 * Returns the first vertical datum found in a coordinate system, 164 * or <code>null</code> if there is none. Note: if a future version, 165 * we may implement this method directly into {@link CoordinateSystem} 166 * (not sure yet if it would be a good idea). 167 */ 168 public static VerticalDatum getVerticalDatum( final CoordinateSystem cs ) { 169 if ( cs instanceof VerticalCoordinateSystem ) { 170 return ( (VerticalCoordinateSystem) cs ).getVerticalDatum(); 171 } 172 if ( cs instanceof CompoundCoordinateSystem ) { 173 VerticalDatum datum; 174 final CompoundCoordinateSystem comp = (CompoundCoordinateSystem) cs; 175 if ( ( datum = getVerticalDatum( comp.getHeadCS() ) ) != null ) 176 return datum; 177 if ( ( datum = getVerticalDatum( comp.getTailCS() ) ) != null ) 178 return datum; 179 } 180 return null; 181 } 182 183 /** 184 * Returns the first temporal datum found in a coordinate system, 185 * or <code>null</code> if there is none. Note: if a future version, 186 * we may implement this method directly into {@link CoordinateSystem} 187 * (not sure yet if it would be a good idea). 188 */ 189 public static TemporalDatum getTemporalDatum( final CoordinateSystem cs ) { 190 if ( cs instanceof TemporalCoordinateSystem ) { 191 return ( (TemporalCoordinateSystem) cs ).getTemporalDatum(); 192 } 193 if ( cs instanceof CompoundCoordinateSystem ) { 194 TemporalDatum datum; 195 final CompoundCoordinateSystem comp = (CompoundCoordinateSystem) cs; 196 if ( ( datum = getTemporalDatum( comp.getHeadCS() ) ) != null ) 197 return datum; 198 if ( ( datum = getTemporalDatum( comp.getTailCS() ) ) != null ) 199 return datum; 200 } 201 return null; 202 } 203 204 /** 205 * Transform an envelope. The transformation is only approximative. 206 * 207 * @param transform The transform to use. 208 * @param envelope Envelope to transform. This envelope will not be modified. 209 * @return The transformed envelope. It may not have the same number of dimensions 210 * than the original envelope. 211 * @throws TransformException if a transform failed. 212 */ 213 public static Envelope transform( final MathTransform transform, final Envelope envelope ) 214 throws TransformException { 215 final int sourceDim = transform.getDimSource(); 216 //final int targetDim = transform.getDimTarget(); 217 if ( envelope.getDimension() != sourceDim ) { 218 throw new MismatchedDimensionException( sourceDim, envelope.getDimension() ); 219 } 220 int coordinateNumber = 0; 221 Envelope transformed = null; 222 CoordinatePoint targetPt = null; 223 final CoordinatePoint sourcePt = new CoordinatePoint( sourceDim ); 224 for ( int i = sourceDim; --i >= 0; ) 225 sourcePt.ord[i] = envelope.getMinimum( i ); 226 227 loop: do { 228 // Transform a point and add the transformed 229 // point to the destination envelope. 230 targetPt = transform.transform( sourcePt, targetPt ); 231 if ( transformed != null ) 232 transformed.add( targetPt ); 233 else 234 transformed = new Envelope( targetPt, targetPt ); 235 236 // Get the next point's coordinate. The 'coordinateNumber' variable should 237 // be seen as a number in base 3 where the number of digits is equals to the 238 // number of dimensions. For example, a 4-D space would have numbers ranging 239 // from "0000" to "2222". The digits are then translated into minimal, central 240 // or maximal ordinates. 241 int n = ++coordinateNumber; 242 for ( int i = sourceDim; --i >= 0; ) { 243 switch ( n % 3 ) { 244 case 0: 245 sourcePt.ord[i] = envelope.getMinimum( i ); 246 n /= 3; 247 break; 248 case 1: 249 sourcePt.ord[i] = envelope.getCenter( i ); 250 continue loop; 251 case 2: 252 sourcePt.ord[i] = envelope.getMaximum( i ); 253 continue loop; 254 } 255 } 256 break; 257 } while ( true ); 258 return transformed; 259 } 260 261 /** 262 * Transform an envelope. The transformation is only approximative. 263 * Invoking this method is equivalent to invoking the following: 264 * <br> 265 * <pre>transform(transform, new Envelope(source)).toRectangle2D()</pre> 266 * 267 * @param transform The transform to use. Source and target dimension must be 2. 268 * @param source The rectangle to transform (may be <code>null</code>). 269 * @param dest The destination rectangle (may be <code>source</code>). 270 * If <code>null</code>, a new rectangle will be created and returned. 271 * @return <code>dest</code>, or a new rectangle if <code>dest</code> was non-null 272 * and <code>source</code> was null. 273 * @throws TransformException if a transform failed. 274 */ 275 public static Rectangle2D transform( final MathTransform2D transform, final Rectangle2D source, 276 final Rectangle2D dest ) 277 throws TransformException { 278 if ( source == null ) { 279 return null; 280 } 281 double xmin = Double.POSITIVE_INFINITY; 282 double ymin = Double.POSITIVE_INFINITY; 283 double xmax = Double.NEGATIVE_INFINITY; 284 double ymax = Double.NEGATIVE_INFINITY; 285 final Point2D.Double point = new Point2D.Double(); 286 for ( int i = 0; i < 8; i++ ) { 287 /* 288 * (0)----(5)----(1) 289 * | | 290 * (4) (7) 291 * | | 292 * (2)----(6)----(3) 293 */ 294 point.x = ( i & 1 ) == 0 ? source.getMinX() : source.getMaxX(); 295 point.y = ( i & 2 ) == 0 ? source.getMinY() : source.getMaxY(); 296 switch ( i ) { 297 case 5: // fallthrough 298 case 6: 299 point.x = source.getCenterX(); 300 break; 301 case 7: // fallthrough 302 case 4: 303 point.y = source.getCenterY(); 304 break; 305 } 306 transform.transform( point, point ); 307 if ( point.x < xmin ) 308 xmin = point.x; 309 if ( point.x > xmax ) 310 xmax = point.x; 311 if ( point.y < ymin ) 312 ymin = point.y; 313 if ( point.y > ymax ) 314 ymax = point.y; 315 } 316 if ( dest != null ) { 317 dest.setRect( xmin, ymin, xmax - xmin, ymax - ymin ); 318 return dest; 319 } 320 return new XRectangle2D( xmin, ymin, xmax - xmin, ymax - ymin ); 321 } 322 323 /** 324 * Retourne une cha�ne de caract�res repr�sentant la r�gion g�ographique sp�cifi�e. La 325 * 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 326 * une projection cartographique est n�cessaire pour obtenir cette repr�sentation, elle 327 * sera faite automatiquement. Cette cha�ne sert surtout � des fins de d�boguage et sa 328 * forme peut varier. 329 */ 330 public static String toWGS84String( final CoordinateSystem cs, Rectangle2D bounds ) { 331 StringBuffer buffer = new StringBuffer(); 332 try { 333 if ( !GeographicCoordinateSystem.WGS84.equivalents( cs ) ) { 334 final CoordinateTransformation tr = CoordinateTransformationFactory.getDefault().createFromCoordinateSystems( 335 cs, 336 GeographicCoordinateSystem.WGS84 ); 337 bounds = transform( (MathTransform2D) tr.getMathTransform(), bounds, null ); 338 } 339 final AngleFormat fmt = new AngleFormat( "DD�MM.m'" ); 340 buffer = fmt.format( new Latitude( bounds.getMinY() ), buffer, null ); 341 buffer.append( '-' ); 342 buffer = fmt.format( new Latitude( bounds.getMaxY() ), buffer, null ); 343 buffer.append( ' ' ); 344 buffer = fmt.format( new Longitude( bounds.getMinX() ), buffer, null ); 345 buffer.append( '-' ); 346 buffer = fmt.format( new Longitude( bounds.getMaxX() ), buffer, null ); 347 } catch ( TransformException exception ) { 348 buffer.append( Utilities.getShortClassName( exception ) ); 349 final String message = exception.getLocalizedMessage(); 350 if ( message != null ) { 351 buffer.append( ": " ); 352 buffer.append( message ); 353 } 354 } 355 return buffer.toString(); 356 } 357 }