//$Header: /home/deegree/jail/deegreerepository/deegree/src/org/deegree/framework/util/MapUtils.java,v 1.7 2006/11/28 13:44:56 mays Exp $
/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2006 by:
EXSE, Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/deegree/
lat/lon GmbH
http://www.lat-lon.de
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
Prof. Dr. Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: greve@giub.uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.framework.util;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.i18n.Messages;
import org.deegree.model.crs.CRSFactory;
import org.deegree.model.crs.CoordinateSystem;
import org.deegree.model.crs.GeoTransformer;
import org.deegree.model.crs.IGeoTransformer;
import org.deegree.model.spatialschema.Envelope;
import org.deegree.model.spatialschema.GeometryFactory;
import org.deegree.model.spatialschema.Position;
/**
*
*
* @version $Revision: 1.7 $
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
* @author last edited by: $Author: mays $
*
* @version 1.0. $Revision: 1.7 $, $Date: 2006/11/28 13:44:56 $
*
* @since 2.0
*/
public class MapUtils {
private static ILogger LOG = LoggerFactory.getLogger( MapUtils.class );
/**
* The Value of sqrt(2)
*/
public static final double SQRT2 = 1.4142135623730950488016887242096980785;
/**
* The Value of a PixelSize
*/
public static final double DEFAULT_PIXEL_SIZE = 0.00028;
/**
* calculates the map scale (denominator) as defined in the OGC SLD 1.0.0
* specification
*
* @param mapWidth map width in pixel
* @param mapHeight map height in pixel
* @param bbox bounding box of the map
* @param crs coordinate reference system of the map
* @param pixelSize size of one pixel of the map measured in meter
*
* @return a maps scale based on the diagonal size of a pixel at the
* center of the map in meter.
* @throws RuntimeException
*/
public static double calcScale( int mapWidth, int mapHeight, Envelope bbox,
CoordinateSystem crs, double pixelSize )
throws RuntimeException {
if( mapWidth == 0 || mapHeight == 0) {
return 0;
}
double scale = 0;
CoordinateSystem cs = crs;
if ( cs == null ) {
throw new RuntimeException( "Invalid crs: " + crs );
}
try {
if ( "m".equals( cs.getUnits() ) ) {
/*
this method to calculate a maps scale as defined in OGC WMS
and SLD specification is not required for maps having a projected
reference system. Direct calculation of scale avoids uncertaincies
*/
// double tmp = Math.pow( mapWidth, 2 ) + Math.pow( mapHeight, 2 );
// double d1 = Math.sqrt( tmp );
// tmp = Math.pow( bbox.getWidth(), 2 ) + Math.pow( bbox.getHeight(), 2 );
// double d2 = Math.sqrt( tmp );
// scale = ( d2 / d1 ) / ( pixelSize / SQRT2 );
double bboxWidth = bbox.getWidth();
double bboxHeight = bbox.getHeight();
double d1 = (mapWidth*mapWidth) + (mapHeight*mapHeight); //Math.sqrt( tmp );
double d2 = (bboxWidth*bboxWidth) + (bboxHeight*bboxHeight);//Math.sqrt( tmp );
scale = Math.sqrt( d2 / d1 ) * ( SQRT2 / pixelSize );
} else {
if ( !crs.getName().equalsIgnoreCase( "EPSG:4326" ) ) {
// transform the bounding box of the request to EPSG:4326
IGeoTransformer trans = new GeoTransformer( CRSFactory.create( "EPSG:4326" ) );
bbox = trans.transform( bbox, crs );
}
double dx = bbox.getWidth() / mapWidth;
double dy = bbox.getHeight() / mapHeight;
Position min = GeometryFactory.createPosition( bbox.getMin().getX()
+ dx * ( mapWidth / 2d - 1 ),
bbox.getMin().getY()
+ dy * ( mapHeight / 2d - 1 ) );
Position max = GeometryFactory.createPosition( bbox.getMin().getX()
+ dx * ( mapWidth / 2d ),
bbox.getMin().getY()
+ dy * ( mapHeight / 2d ) );
double distance = calcDistance( min.getX(), min.getY(), max.getX(), max.getY() );
scale = distance * ( SQRT2 / pixelSize );
}
} catch ( Exception e ) {
LOG.logError( e.getMessage(), e );
throw new RuntimeException( Messages.getMessage( "FRAMEWORK_ERROR_SCALE_CALC", e.getMessage() ) );
}
return scale;
}
/**
* calculates the distance in meters between two points in EPSG:4326 coodinates.
* this is a convenience method assuming the world is a ball
* @param lon1
* @param lat1
* @param lon2
* @param lat2
* @return the distance in meters between two points in EPSG:4326 coords
*/
public static double calcDistance( double lon1, double lat1, double lon2, double lat2 ) {
double r = 6378.137;
double rad = Math.PI / 180d;
double cose = Math.sin( rad * lon1 ) * Math.sin( rad * lon2 ) + Math.cos( rad * lon1 )
* Math.cos( rad * lon2 ) * Math.cos( rad * ( lat1 - lat2 ) );
double dist = r * Math.acos( cose ) * Math.cos( rad * Math.min( lat1, lat2 ) ) ;
// * 0.75 is just an heuristic correction factor
return dist * 1000 * 0.75;
}
/**
* The method calculates a new Envelope from the <code>requestedBarValue</code> It will either
* zoom in or zoom out of the <code>actualBBOX<code> depending
* on the ratio of the <code>requestedBarValue</code> to the <code>actualBarValue</code>
* @param currentEnvelope current Envelope
* @param currentScale the scale of the current envelope
* @param requestedScale requested scale value
* @return a new Envelope
*/
public static Envelope scaleEnvelope( Envelope currentEnvelope, double currentScale,
double requestedScale ) {
double ratio = requestedScale / currentScale;
double newWidth = currentEnvelope.getWidth() * ratio;
double newHeight = currentEnvelope.getHeight() * ratio;
double midX = currentEnvelope.getMin().getX() + ( currentEnvelope.getWidth() / 2d );
double midY = currentEnvelope.getMin().getY() + ( currentEnvelope.getWidth() / 2d );
double minx = midX - newWidth / 2d;
double maxx = midX + newWidth / 2d;
double miny = midY - newHeight / 2d;
double maxy = midY + newHeight / 2d;
return GeometryFactory.createEnvelope( minx, miny, maxx, maxy,
currentEnvelope.getCoordinateSystem() );
}
/**
* This method ensures the bbox is resized (shrunk) to match the aspect ratio defined by
* mapHeight/mapWidth
*
* @param bbox
* @param mapWith
* @param mapHeight
* @return a new bounding box with the aspect ratio given my mapHeight/mapWidth
*/
public static final Envelope ensureAspectRatio( Envelope bbox, double mapWith, double mapHeight ) {
double minx = bbox.getMin().getX();
double miny = bbox.getMin().getY();
double maxx = bbox.getMax().getX();
double maxy = bbox.getMax().getY();
double dx = maxx - minx;
double dy = maxy - miny;
double ratio = mapHeight / mapWith;
if ( dx >= dy ) {
// height has to be corrected
double[] normCoords = getNormalizedCoords( dx, ratio, miny, maxy );
miny = normCoords[0];
maxy = normCoords[1];
} else {
// width has to be corrected
ratio = mapWith / mapHeight;
double[] normCoords = getNormalizedCoords( dy, ratio, minx, maxx );
minx = normCoords[0];
maxx = normCoords[1];
}
CoordinateSystem crs = bbox.getCoordinateSystem();
return GeometryFactory.createEnvelope( minx, miny, maxx, maxy, crs );
}
private static final double[] getNormalizedCoords( double normLen, double ratio, double min,
double max ) {
double mid = ( max - min ) / 2 + min;
min = mid - ( normLen / 2 ) * ratio;
max = mid + ( normLen / 2 ) * ratio;
double[] newCoords = { min, max };
return newCoords;
}
}
/* ********************************************************************
Changes to this class. What the people have been up to:
$Log: MapUtils.java,v $
Revision 1.7 2006/11/28 13:44:56 mays
add method ensureAspectRatio() and depending method getNormalizedCoords()
Revision 1.6 2006/11/27 09:07:52 poth
JNI integration of proj4 has been removed. The CRS functionality now will be done by native deegree code.
Revision 1.5 2006/11/26 19:02:54 poth
bug fix
Revision 1.4 2006/11/26 18:48:16 poth
bug fix
Revision 1.3 2006/11/23 08:25:14 bezema
changed the scale calculation to be faster, added a SQRT2 and a PIXELSIZE field
Revision 1.1 2006/10/17 20:31:19 poth
*** empty log message ***
Revision 1.23 2006/09/27 16:46:41 poth
transformation method signature changed
Revision 1.22 2006/09/26 14:21:17 poth
bug fix
Revision 1.21 2006/09/26 13:31:11 poth
bug fix
Revision 1.20 2006/09/26 13:29:55 poth
bug fix
Revision 1.19 2006/09/26 13:16:43 poth
LOG statement added
Revision 1.18 2006/09/25 20:28:53 poth
bug fixes - map scale calculation
Revision 1.17 2006/09/25 12:47:00 poth
bug fixes - map scale calculation
Revision 1.16 2006/09/22 09:07:58 taddei
refactored: added scaleEnvelope to this class, becaus it used by many classes
Revision 1.15 2006/09/18 12:37:22 bezema
fixed a division by null
Revision 1.14 2006/07/28 09:24:39 poth
bug fix - calculating geographic distance
Revision 1.13 2006/07/25 06:21:53 poth
bug fix - calculating map scale
Revision 1.12 2006/07/11 13:47:52 bezema
changed the throws exception to throws RuntimeException
Revision 1.11 2006/07/11 13:39:02 taddei
*** empty log message ***
Revision 1.10 2006/05/31 12:19:34 poth
*** empty log message ***
Revision 1.9 2006/05/31 11:57:37 poth
bug fix - pixel size considered
Revision 1.8 2006/05/31 09:17:05 ncho
SN: calcDistance-changed private to public
and added NullPointerException
Revision 1.7 2006/05/03 20:09:52 poth
*** empty log message ***
Revision 1.6 2006/05/01 20:15:27 poth
*** empty log message ***
Revision 1.5 2006/04/06 20:25:28 poth
*** empty log message ***
Revision 1.4 2006/03/30 21:20:27 poth
*** empty log message ***
Revision 1.3 2005/08/19 07:45:11 poth
no message
Revision 1.2 2005/08/05 09:42:20 poth
no message
Revision 1.1 2005/07/30 18:11:20 poth
no message
********************************************************************** */