001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/framework/util/MapUtils.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2006 by: 006 EXSE, Department of Geography, University of Bonn 007 http://www.giub.uni-bonn.de/deegree/ 008 lat/lon GmbH 009 http://www.lat-lon.de 010 011 This library is free software; you can redistribute it and/or 012 modify it under the terms of the GNU Lesser General Public 013 License as published by the Free Software Foundation; either 014 version 2.1 of the License, or (at your option) any later version. 015 016 This library is distributed in the hope that it will be useful, 017 but WITHOUT ANY WARRANTY; without even the implied warranty of 018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 019 Lesser General Public License for more details. 020 021 You should have received a copy of the GNU Lesser General Public 022 License along with this library; if not, write to the Free Software 023 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 024 025 Contact: 026 027 Andreas Poth 028 lat/lon GmbH 029 Aennchenstr. 19 030 53115 Bonn 031 Germany 032 E-Mail: poth@lat-lon.de 033 034 Prof. Dr. Klaus Greve 035 Department of Geography 036 University of Bonn 037 Meckenheimer Allee 166 038 53115 Bonn 039 Germany 040 E-Mail: greve@giub.uni-bonn.de 041 042 ---------------------------------------------------------------------------*/ 043 package org.deegree.framework.util; 044 045 import org.deegree.framework.log.ILogger; 046 import org.deegree.framework.log.LoggerFactory; 047 import org.deegree.i18n.Messages; 048 import org.deegree.model.crs.CRSFactory; 049 import org.deegree.model.crs.CoordinateSystem; 050 import org.deegree.model.crs.GeoTransformer; 051 import org.deegree.model.crs.IGeoTransformer; 052 import org.deegree.model.spatialschema.Envelope; 053 import org.deegree.model.spatialschema.GeometryFactory; 054 import org.deegree.model.spatialschema.Position; 055 056 /** 057 * 058 * 059 * @version $Revision: 6259 $ 060 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 061 * @author last edited by: $Author: bezema $ 062 * 063 * @version 1.0. $Revision: 6259 $, $Date: 2007-03-20 10:15:15 +0100 (Di, 20 Mär 2007) $ 064 * 065 * @since 2.0 066 */ 067 public class MapUtils { 068 069 private static ILogger LOG = LoggerFactory.getLogger( MapUtils.class ); 070 071 /** 072 * The Value of sqrt(2) 073 */ 074 public static final double SQRT2 = 1.4142135623730950488016887242096980785; 075 076 /** 077 * The Value of a PixelSize 078 */ 079 public static final double DEFAULT_PIXEL_SIZE = 0.00028; 080 081 /** 082 * calculates the map scale (denominator) as defined in the OGC SLD 1.0.0 083 * specification 084 * 085 * @param mapWidth map width in pixel 086 * @param mapHeight map height in pixel 087 * @param bbox bounding box of the map 088 * @param crs coordinate reference system of the map 089 * @param pixelSize size of one pixel of the map measured in meter 090 * 091 * @return a maps scale based on the diagonal size of a pixel at the 092 * center of the map in meter. 093 * @throws RuntimeException 094 */ 095 public static double calcScale( int mapWidth, int mapHeight, Envelope bbox, 096 CoordinateSystem crs, double pixelSize ) 097 throws RuntimeException { 098 099 if( mapWidth == 0 || mapHeight == 0) { 100 return 0; 101 } 102 103 double scale = 0; 104 105 CoordinateSystem cs = crs; 106 107 if ( cs == null ) { 108 throw new RuntimeException( "Invalid crs: " + crs ); 109 } 110 111 try { 112 if ( "m".equals( cs.getUnits() ) ) { 113 114 /* 115 this method to calculate a maps scale as defined in OGC WMS 116 and SLD specification is not required for maps having a projected 117 reference system. Direct calculation of scale avoids uncertaincies 118 */ 119 // double tmp = Math.pow( mapWidth, 2 ) + Math.pow( mapHeight, 2 ); 120 // double d1 = Math.sqrt( tmp ); 121 // tmp = Math.pow( bbox.getWidth(), 2 ) + Math.pow( bbox.getHeight(), 2 ); 122 // double d2 = Math.sqrt( tmp ); 123 // scale = ( d2 / d1 ) / ( pixelSize / SQRT2 ); 124 double bboxWidth = bbox.getWidth(); 125 double bboxHeight = bbox.getHeight(); 126 double d1 = (mapWidth*mapWidth) + (mapHeight*mapHeight); //Math.sqrt( tmp ); 127 double d2 = (bboxWidth*bboxWidth) + (bboxHeight*bboxHeight);//Math.sqrt( tmp ); 128 scale = Math.sqrt( d2 / d1 ) * ( SQRT2 / pixelSize ); 129 130 } else { 131 132 if ( !crs.getName().equalsIgnoreCase( "EPSG:4326" ) ) { 133 // transform the bounding box of the request to EPSG:4326 134 IGeoTransformer trans = new GeoTransformer( CRSFactory.create( "EPSG:4326" ) ); 135 bbox = trans.transform( bbox, crs ); 136 } 137 double dx = bbox.getWidth() / mapWidth; 138 double dy = bbox.getHeight() / mapHeight; 139 Position min = GeometryFactory.createPosition( bbox.getMin().getX() 140 + dx * ( mapWidth / 2d - 1 ), 141 bbox.getMin().getY() 142 + dy * ( mapHeight / 2d - 1 ) ); 143 Position max = GeometryFactory.createPosition( bbox.getMin().getX() 144 + dx * ( mapWidth / 2d ), 145 bbox.getMin().getY() 146 + dy * ( mapHeight / 2d ) ); 147 148 double distance = calcDistance( min.getX(), min.getY(), max.getX(), max.getY() ); 149 150 scale = distance * ( SQRT2 / pixelSize ); 151 152 } 153 } catch ( Exception e ) { 154 LOG.logError( e.getMessage(), e ); 155 throw new RuntimeException( Messages.getMessage( "FRAMEWORK_ERROR_SCALE_CALC", e.getMessage() ) ); 156 } 157 158 return scale; 159 160 } 161 162 /** 163 * calculates the distance in meters between two points in EPSG:4326 coodinates. 164 * this is a convenience method assuming the world is a ball 165 * @param lon1 166 * @param lat1 167 * @param lon2 168 * @param lat2 169 * @return the distance in meters between two points in EPSG:4326 coords 170 */ 171 public static double calcDistance( double lon1, double lat1, double lon2, double lat2 ) { 172 double r = 6378.137; 173 double rad = Math.PI / 180d; 174 double cose = Math.sin( rad * lon1 ) * Math.sin( rad * lon2 ) + Math.cos( rad * lon1 ) 175 * Math.cos( rad * lon2 ) * Math.cos( rad * ( lat1 - lat2 ) ); 176 double dist = r * Math.acos( cose ) * Math.cos( rad * Math.min( lat1, lat2 ) ) ; 177 178 // * 0.75 is just an heuristic correction factor 179 return dist * 1000 * 0.75; 180 } 181 182 183 /** 184 * The method calculates a new Envelope from the <code>requestedBarValue</code> It will either 185 * zoom in or zoom out of the <code>actualBBOX<code> depending 186 * on the ratio of the <code>requestedBarValue</code> to the <code>actualBarValue</code> 187 * @param currentEnvelope current Envelope 188 * @param currentScale the scale of the current envelope 189 * @param requestedScale requested scale value 190 * @return a new Envelope 191 */ 192 public static Envelope scaleEnvelope( Envelope currentEnvelope, double currentScale, 193 double requestedScale ) { 194 195 double ratio = requestedScale / currentScale; 196 double newWidth = currentEnvelope.getWidth() * ratio; 197 double newHeight = currentEnvelope.getHeight() * ratio; 198 double midX = currentEnvelope.getMin().getX() + ( currentEnvelope.getWidth() / 2d ); 199 double midY = currentEnvelope.getMin().getY() + ( currentEnvelope.getHeight() / 2d ); 200 201 double minx = midX - newWidth / 2d; 202 double maxx = midX + newWidth / 2d; 203 double miny = midY - newHeight / 2d; 204 double maxy = midY + newHeight / 2d; 205 206 return GeometryFactory.createEnvelope( minx, miny, maxx, maxy, 207 currentEnvelope.getCoordinateSystem() ); 208 209 } 210 211 /** 212 * This method ensures the bbox is resized (shrunk) to match the aspect ratio defined by 213 * mapHeight/mapWidth 214 * 215 * @param bbox 216 * @param mapWith 217 * @param mapHeight 218 * @return a new bounding box with the aspect ratio given my mapHeight/mapWidth 219 */ 220 public static final Envelope ensureAspectRatio( Envelope bbox, double mapWith, double mapHeight ) { 221 222 double minx = bbox.getMin().getX(); 223 double miny = bbox.getMin().getY(); 224 double maxx = bbox.getMax().getX(); 225 double maxy = bbox.getMax().getY(); 226 227 double dx = maxx - minx; 228 double dy = maxy - miny; 229 230 double ratio = mapHeight / mapWith; 231 232 if ( dx >= dy ) { 233 // height has to be corrected 234 double[] normCoords = getNormalizedCoords( dx, ratio, miny, maxy ); 235 miny = normCoords[0]; 236 maxy = normCoords[1]; 237 } else { 238 // width has to be corrected 239 ratio = mapWith / mapHeight; 240 double[] normCoords = getNormalizedCoords( dy, ratio, minx, maxx ); 241 minx = normCoords[0]; 242 maxx = normCoords[1]; 243 } 244 CoordinateSystem crs = bbox.getCoordinateSystem(); 245 246 return GeometryFactory.createEnvelope( minx, miny, maxx, maxy, crs ); 247 } 248 249 private static final double[] getNormalizedCoords( double normLen, double ratio, double min, 250 double max ) { 251 double mid = ( max - min ) / 2 + min; 252 min = mid - ( normLen / 2 ) * ratio; 253 max = mid + ( normLen / 2 ) * ratio; 254 double[] newCoords = { min, max }; 255 return newCoords; 256 } 257 258 } 259 260 /* ******************************************************************** 261 Changes to this class. What the people have been up to: 262 $Log$ 263 Revision 1.8 2006/12/13 16:07:06 mays 264 bugfix in scaleEnvelope: change from getWidth() to getHeight() for Y-coordinates 265 266 Revision 1.7 2006/11/28 13:44:56 mays 267 add method ensureAspectRatio() and depending method getNormalizedCoords() 268 269 Revision 1.6 2006/11/27 09:07:52 poth 270 JNI integration of proj4 has been removed. The CRS functionality now will be done by native deegree code. 271 272 Revision 1.5 2006/11/26 19:02:54 poth 273 bug fix 274 275 Revision 1.4 2006/11/26 18:48:16 poth 276 bug fix 277 278 Revision 1.3 2006/11/23 08:25:14 bezema 279 changed the scale calculation to be faster, added a SQRT2 and a PIXELSIZE field 280 281 Revision 1.1 2006/10/17 20:31:19 poth 282 *** empty log message *** 283 284 Revision 1.23 2006/09/27 16:46:41 poth 285 transformation method signature changed 286 287 Revision 1.22 2006/09/26 14:21:17 poth 288 bug fix 289 290 Revision 1.21 2006/09/26 13:31:11 poth 291 bug fix 292 293 Revision 1.20 2006/09/26 13:29:55 poth 294 bug fix 295 296 Revision 1.19 2006/09/26 13:16:43 poth 297 LOG statement added 298 299 Revision 1.18 2006/09/25 20:28:53 poth 300 bug fixes - map scale calculation 301 302 Revision 1.17 2006/09/25 12:47:00 poth 303 bug fixes - map scale calculation 304 305 Revision 1.16 2006/09/22 09:07:58 taddei 306 refactored: added scaleEnvelope to this class, becaus it used by many classes 307 308 Revision 1.15 2006/09/18 12:37:22 bezema 309 fixed a division by null 310 311 Revision 1.14 2006/07/28 09:24:39 poth 312 bug fix - calculating geographic distance 313 314 Revision 1.13 2006/07/25 06:21:53 poth 315 bug fix - calculating map scale 316 317 Revision 1.12 2006/07/11 13:47:52 bezema 318 changed the throws exception to throws RuntimeException 319 320 Revision 1.11 2006/07/11 13:39:02 taddei 321 *** empty log message *** 322 323 Revision 1.10 2006/05/31 12:19:34 poth 324 *** empty log message *** 325 326 Revision 1.9 2006/05/31 11:57:37 poth 327 bug fix - pixel size considered 328 329 Revision 1.8 2006/05/31 09:17:05 ncho 330 SN: calcDistance-changed private to public 331 and added NullPointerException 332 333 Revision 1.7 2006/05/03 20:09:52 poth 334 *** empty log message *** 335 336 Revision 1.6 2006/05/01 20:15:27 poth 337 *** empty log message *** 338 339 Revision 1.5 2006/04/06 20:25:28 poth 340 *** empty log message *** 341 342 Revision 1.4 2006/03/30 21:20:27 poth 343 *** empty log message *** 344 345 Revision 1.3 2005/08/19 07:45:11 poth 346 no message 347 348 Revision 1.2 2005/08/05 09:42:20 poth 349 no message 350 351 Revision 1.1 2005/07/30 18:11:20 poth 352 no message 353 354 355 ********************************************************************** */