//$Header: /home/deegree/jail/deegreerepository/deegree/src/org/deegree/ogcwebservices/wpvs/j3d/ViewPoint.java,v 1.13 2006/12/04 17:06:43 bezema 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
Aennchenstraße 19
53177 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.ogcwebservices.wpvs.j3d;
import javax.media.j3d.Transform3D;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.model.crs.CoordinateSystem;
import org.deegree.model.spatialschema.Envelope;
import org.deegree.model.spatialschema.GeometryException;
import org.deegree.model.spatialschema.GeometryFactory;
import org.deegree.model.spatialschema.Position;
import org.deegree.model.spatialschema.Surface;
import org.deegree.model.spatialschema.WKTAdapter;
import org.deegree.ogcwebservices.wpvs.operation.GetView;
/**
* This class represents the view point for a WPVS request. That is, it represents the point where
* the observer is at, and looking to a target point. An angle of view must be also given.
*
* @author <a href="mailto:lupp@lat-lon.de">Katharina Lupp</a>
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
*
* @author last edited by: $Author: bezema $
* @version $Revision: 1.13 $ $Date: 2006/12/04 17:06:43 $
*/
public class ViewPoint {
private static final ILogger LOG = LoggerFactory.getLogger( ViewPoint.class );
private static final double rad0 = Math.toRadians( 0 );
private static final double rad90 = Math.toRadians( 90 );
private static final double rad180 = Math.toRadians( 180 );
private static final double rad270 = Math.toRadians( 270 );
private static final double rad360 = Math.toRadians( 360 );
private CoordinateSystem crs;
private Point3d observerPosition;
private Point3d pointOfInterest;
private Point3d[] footprint;
private double angleOfView = 0;
private double yaw = 0;
private double pitch = 0;
private double terrainDistanceToSeaLevel = 0;
private double viewerToPOIDistance = 0;
private double farClippingPlane = 0;
private Transform3D simpleTransform = null;
private Transform3D viewMatrix = null;
/**
* Creates a new instance of ViewPoint_Impl
*
* @param yaw
* rotation on the Z-Axis in radians of the viewer
* @param pitch
* rotation on the X-Axis in radians
* @param viewerToPOIDistance
* from the point of interest to the viewersposition
* @param pointOfInterest
* the point of interest
* @param angleOfView
* @param farClippingPlane
* where the view ends
* @param distanceToSealevel
* @param crs
* The Coordinatesystem in which the given reside
*/
public ViewPoint( double yaw, double pitch, double viewerToPOIDistance,
Point3d pointOfInterest, double angleOfView, double farClippingPlane,
double distanceToSealevel, CoordinateSystem crs ) {
this.yaw = yaw;
this.pitch = pitch;
// pitch and aov can not have the same value (the footprint will be one) therefor a little
// check and correction
if ( Math.abs( pitch - angleOfView ) <= 0.01 )
this.pitch -= Math.toRadians( 0.5 );
this.angleOfView = angleOfView;
this.pointOfInterest = pointOfInterest;
this.viewerToPOIDistance = viewerToPOIDistance;
this.farClippingPlane = farClippingPlane;
this.terrainDistanceToSeaLevel = distanceToSealevel;
this.crs = crs;
simpleTransform = new Transform3D();
viewMatrix = new Transform3D();
observerPosition = new Point3d();
footprint = new Point3d[4];
calcObserverPosition();
}
/**
* @param request
* a server request.
*/
public ViewPoint( GetView request ) {
this( request.getYaw(), request.getPitch(), request.getDistance(),
request.getPointOfInterest(), request.getAngleOfView(),
request.getFarClippingPlane(), 0, request.getCrs() );
}
/**
* @param request
* a server request.
* @param distanceToSeaLevel
*/
public ViewPoint( GetView request, double distanceToSeaLevel ) {
this( request.getYaw(), request.getPitch(), request.getDistance(),
request.getPointOfInterest(), request.getAngleOfView(),
request.getFarClippingPlane(), distanceToSeaLevel,
request.getCrs() );
}
/**
* Calculates the observers position for a given pointOfInterest, distance and view direction(
* as semi polar coordinates, yaw & pitch ). also recalculating the viewmatrix and the
* footprint, for they are affected by the change of position.
*
*/
private void calcObserverPosition() {
LOG.entering();
double z = Math.sin( pitch ) * this.viewerToPOIDistance;
double groundLength = Math.sqrt( ( viewerToPOIDistance * viewerToPOIDistance ) - ( z * z ) );
double x = Math.sin( yaw ) * groundLength;
// -1 if yaw is null, we're looking to the north
double y = -1 * Math.cos( yaw ) * groundLength;
LOG.exiting();
observerPosition.x = pointOfInterest.x + x;
observerPosition.y = pointOfInterest.y + y;
observerPosition.z = pointOfInterest.z + z;
calcFootprint();
calculateViewMatrix();
}
/**
* calculates the target point of the view for the submitted height
*
* @return the point of interest, given the current viewposition, yaw and pitch.
*/
@SuppressWarnings("unused")
private Point3d calcPointOfInterest() {
LOG.entering();
double heightAboveGround = observerPosition.z - terrainDistanceToSeaLevel;
double distance = heightAboveGround / Math.tan( pitch );
double xDistance = 0; //
double yDistance = 0; // Math.sqrt( ( distance * distance ) - ( yDistance * yDistance ) );
Point3d tp = null;
if ( ( yaw >= rad0 ) && ( yaw <= rad90 ) ) {
xDistance = Math.sin( yaw ) * distance;
yDistance = Math.sqrt( ( distance * distance ) - ( xDistance * xDistance ) );
} else if ( ( yaw > rad90 ) && ( yaw <= rad180 ) ) {
xDistance = Math.sin( rad90 - yaw ) * distance;
yDistance = -1 * ( Math.sqrt( ( distance * distance ) - ( xDistance * xDistance ) ) );
} else if ( ( yaw > rad180 ) && ( yaw <= rad270 ) ) {
xDistance = -1 * ( Math.sin( rad180 - yaw ) * distance );
yDistance = -1 * ( Math.sqrt( ( distance * distance ) - ( xDistance * xDistance ) ) );
} else if ( ( yaw > rad270 ) && ( yaw < rad360 ) ) {
xDistance = -1 * ( Math.sin( rad270 - yaw ) * distance );
yDistance = Math.sqrt( ( distance * distance ) - ( xDistance * xDistance ) );
}
tp = new Point3d( observerPosition.x + xDistance, observerPosition.y + yDistance,
terrainDistanceToSeaLevel );
LOG.exiting();
return tp;
}
/**
* Calculates the field of view aka footprint, the corner points of the field of view as
* follows, <br/>
* <ul>
* <li> f[0] = farclippingplane right side fo viewDirection </li>
* <li> f[1] = farclippingplane left side fo viewDirection </li>
* <li> f[2] = nearclippingplane right side fo viewDirection, note it can be behind the
* viewPosition </li>
* <li> f[3] = nearclippingplane left side fo viewDirection, note it can be behind the
* viewPosition </li>
* </ul>
* <br/> the are rotated and translated according to the simpleTranform
*
*/
private void calcFootprint() {
LOG.entering();
// should be read frome the configuration
// double farClippingPlane = 15000;
double nearClippingPlane = 0;
double farClippingPlaneDistance = farClippingPlane;
double halfAngleOfView = angleOfView * 0.5;
// double heightAboveGround = observerPosition.y - distanceToSeaLevel;
double heightAboveGround = observerPosition.z - terrainDistanceToSeaLevel;
if ( heightAboveGround < 0 ) { // beneath the ground
LOG.logError( "the Observer is below the terrain" );
return;
}
if ( pitch > 0 ) { // the eye is looking down on the poi
// pitch equals angle between upper and viewaxis, angleOfView is centered around the
// viewaxis
double angleToZ = pitch + halfAngleOfView;
if ( Math.abs( angleToZ - rad90 ) > 0.00001 ) {
// footprint front border distance
if ( angleToZ > rad90 ) {
nearClippingPlane = -1 * ( heightAboveGround * Math.tan( angleToZ - rad90 ) );
} else {
nearClippingPlane = heightAboveGround / Math.tan( angleToZ );
}
}
// And the far clippingplane
angleToZ = pitch - halfAngleOfView;
if ( angleToZ > 0 ) {
farClippingPlaneDistance = heightAboveGround / Math.tan( angleToZ );
if ( farClippingPlaneDistance > farClippingPlane )
farClippingPlaneDistance = farClippingPlane;
}
} else {
// TODO looking up to the poi
}
double farX = 0;
if ( Math.abs( ( halfAngleOfView + rad90 ) % rad180 ) > 0.00001 )
farX = Math.tan( halfAngleOfView ) * farClippingPlaneDistance;
else {
// TODO
}
// Although currently not supported in the DataBase, the Altitude (z) is set to
// distanceSeaLevel, who knows what it might bring in the future.
footprint[0] = new Point3d( farX, farClippingPlaneDistance, terrainDistanceToSeaLevel );
footprint[1] = new Point3d( -farX, farClippingPlaneDistance, terrainDistanceToSeaLevel );
double nearX = 0;
if ( Math.abs( ( halfAngleOfView + rad90 ) % rad180 ) > 0.00001 )
nearX = Math.tan( halfAngleOfView ) * nearClippingPlane;
else {
// TODO
}
footprint[2] = new Point3d( nearX, nearClippingPlane, terrainDistanceToSeaLevel );
footprint[3] = new Point3d( -nearX, nearClippingPlane, terrainDistanceToSeaLevel );
// System.out.println( "near->" + nearX );
// System.out.println( "far->" + farX );
// Looking north means yaw
simpleTransform.rotZ( yaw );
// translate to the viewersposition.
simpleTransform.setTranslation( new Vector3d( observerPosition.x, observerPosition.y,
this.terrainDistanceToSeaLevel ) );
for ( Point3d fp : footprint ) {
simpleTransform.transform( fp );
}
LOG.exiting();
}
/**
* Sets the viewMatrix according to the given yaw, pitch and the calculated observerPosition.
*/
private void calculateViewMatrix() {
viewMatrix.setIdentity();
viewMatrix.lookAt( observerPosition, pointOfInterest, new Vector3d( 0, 0, 1 ) );
viewMatrix.invert();
}
/**
* @return true if the near clippingplane is behind the viewposition.
*/
public boolean isNearClippingplaneBehindViewPoint() {
if ( pitch > 0 ) { // the eye is looking down on the poi
// pitch equals angle between upper and viewaxis, angleOfView is centered around the
// viewaxis
double angleToZ = pitch + ( angleOfView * 0.5 );
if ( Math.abs( angleToZ - rad90 ) > 0.00001 ) {
// footprint front border distance
if ( angleToZ > rad90 ) {
return true;
}
}
} else {
// TODO looking up to the poi
}
return false;
}
/**
*
* @return the field of view of the observer in radians
*/
public double getAngleOfView() {
return angleOfView;
}
/**
* @param aov
* the field of view of the observer in radians
*/
public void setAngleOfView( double aov ) {
this.angleOfView = aov;
calcFootprint();
}
/**
*
* @return the horizontal direction in radians the observer looks
*/
public double getYaw() {
return yaw;
}
/**
*
* @param yaw
* the horizontal direction in radians the observer looks
*/
public void setYaw( double yaw ) {
this.yaw = yaw;
calcObserverPosition();
}
/**
* @return vertical direction in radians the observer looks
*/
public double getPitch() {
return pitch;
}
/**
* @param pitch
* the vertical direction in radians the observer looks
*
*/
public void setPitch( double pitch ) {
this.pitch = (pitch%rad90);
// pitch and aov can not have the same value (the footprint will be one) therefor a little
// check and correction
if ( Math.abs( pitch - angleOfView ) <= 0.01 )
this.pitch -= Math.toRadians( 0.5 );
calcObserverPosition();
}
/**
* @return Returns the distanceToSeaLevel of the terrain beneath the viewpoint.
*/
public double getTerrainDistanceToSeaLevel() {
return terrainDistanceToSeaLevel;
}
/**
* @param distanceToSeaLevel
* of the terrain beneath the viewpoint
*/
public void setTerrainDistanceToSeaLevel( double distanceToSeaLevel ) {
this.terrainDistanceToSeaLevel = distanceToSeaLevel;
calcFootprint();
}
/**
* @return the position of the observer, the directions he looks and his field of view in
* radians
*
*/
public Point3d getObserverPosition() {
return observerPosition;
}
/**
* @param observerPosition
* the position of the observer, the directions he looks and his field of view in
* radians
*
*/
public void setObserverPosition( Point3d observerPosition ) {
this.observerPosition = observerPosition;
calcFootprint();
calculateViewMatrix();
}
/**
* @return the point of interest to which the viewer is looking
*/
public Point3d getPointOfInterest() {
return pointOfInterest;
}
/**
* @param pointOfInterest
* the directions the observer looks and his field of view in radians
*
*/
public void setPointOfInterest( Point3d pointOfInterest ) {
this.pointOfInterest = pointOfInterest;
calcObserverPosition();
}
/**
* The footprint in object space: <br/>f[0] = (FarclippingPlaneRight) = angleOfView/2 +
* viewDirection.x, farclippingplaneDistance, distanceToSealevel <br/>f[1] =
* (FarclippingPlaneLeft) = angleOfView/2 - viewDirection.x, farclippingplaneDistance,
* distanceToSealevel <br/>f[2] = (NearclippingPlaneRight) = angleOfView/2 + viewDirection.x,
* nearclippingplaneDistance, distanceToSealevel <br/>f[3] = (NearclippingPlaneLeft) =
* angleOfView/2 - viewDirection.x, nearclippingplaneDistance, distanceToSealevel
*
* @return footprint or rather the field of view
*/
public Point3d[] getFootprint() {
return footprint;
}
/**
* @param distanceToSeaLevel
* the new height for which the footprint should be calculated
* @return footprint or rather the field of view
*/
public Point3d[] getFootprint( double distanceToSeaLevel ) {
this.terrainDistanceToSeaLevel = distanceToSeaLevel;
calcFootprint();
return footprint;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append( "observerPosition: " + observerPosition + "\n" );
sb.append( "targetPoint: " + pointOfInterest + "\n" );
sb.append( "footprint: " + "\n" );
sb.append( footprint[0] + "\n" );
sb.append( footprint[1] + "\n" );
sb.append( footprint[2] + "\n" );
sb.append( footprint[3] + "\n" );
sb.append( "aov: " + Math.toDegrees( angleOfView ) + "\n" );
sb.append( "yaw: " + Math.toDegrees( yaw ) + "\n" );
sb.append( "pitch: " + Math.toDegrees( pitch ) + "\n" );
sb.append( "distanceToSeaLevel: " + terrainDistanceToSeaLevel + "\n" );
sb.append( "farClippingPlane: " + farClippingPlane + "\n" );
return sb.toString();
}
/**
* @return Returns the farClippingPlane.
*/
public double getFarClippingPlane() {
return farClippingPlane;
}
/**
* @return Returns a new transform3D Object which contains the transformations to place the
* viewers Position and his yaw viewing angle relativ to the 0,0 coordinates and the
* poi.
*/
public Transform3D getSimpleTransform() {
return new Transform3D( simpleTransform );
}
/**
* @param transform
* The transform to set.
*/
public void setSimpleTransform( Transform3D transform ) {
this.simpleTransform = transform;
}
/**
* @return Returns the viewMatrix.
*/
public Transform3D getViewMatrix() {
return viewMatrix;
}
/**
* @return the viewerToPOIDistance value.
*/
public double getViewerToPOIDistance() {
return viewerToPOIDistance;
}
/**
* @param viewerToPOIDistance
* An other viewerToPOIDistance value.
*/
public void setViewerToPOIDistance( double viewerToPOIDistance ) {
this.viewerToPOIDistance = viewerToPOIDistance;
calcObserverPosition();
}
/**
*
* @return the Footprint as a Surface (bbox)
*/
public Surface getVisibleArea() {
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
double maxX = Double.MIN_VALUE;
double maxY = Double.MIN_VALUE;
for ( Point3d point : footprint ) {
if ( point.x < minX )
minX = point.x;
if ( point.x > maxX )
maxX = point.x;
if ( point.y < minY )
minY = point.y;
if ( point.y > maxY )
maxY = point.y;
}
Envelope env = GeometryFactory.createEnvelope( minX, minY, maxX, maxY, crs );
Surface s = null;
try {
s = GeometryFactory.createSurface( env, crs );
} catch ( GeometryException e ) {
e.printStackTrace();
}
return s;
}
/**
* @return A String representation of the Footprint, so that it can be easily used in another
* programm e.g. deejump
* @throws GeometryException
* if the footprint could not be transformed to wkt.
*/
public String getFootPrintAsWellKnownText()
throws GeometryException {
Position[] pos = new Position[footprint.length + 1];
for ( int i = 0; i < footprint.length; ++i ) {
Point3d point = footprint[i];
pos[i] = GeometryFactory.createPosition( point.x, point.y, point.z );
}
Point3d point = footprint[0];
pos[footprint.length] = GeometryFactory.createPosition( point.x, point.y, point.z );
return WKTAdapter.export( GeometryFactory.createSurface( pos, null, null, crs ) ).toString();
}
/**
* @return the ObserverPosition as a well known text String.
* @throws GeometryException if the conversion fails.
*/
public String getObserverPositionAsWKT()
throws GeometryException {
return WKTAdapter.export(
GeometryFactory.createPoint( observerPosition.x,
observerPosition.y,
observerPosition.z, crs ) ).toString();
}
/**
* @return the crs value.
*/
public CoordinateSystem getCrs() {
return crs;
}
}
/***************************************************************************************************
* Changes to this class. What the people have been up to: $Log: ViewPoint.java,v $
* Changes to this class. What the people have been up to: Revision 1.13 2006/12/04 17:06:43 bezema
* Changes to this class. What the people have been up to: enhanced dgm from wcs support
* Changes to this class. What the people have been up to:
* Changes to this class. What the people have been up to: Revision 1.12 2006/11/29 16:01:17 bezema
* Changes to this class. What the people have been up to: bug fixes and added features
* Changes to this class. What the people have been up to:
* Changes to this class. What the people have been up to: Revision 1.11 2006/11/27 15:42:34 bezema
* Changes to this class. What the people have been up to: Updated the coordinatesystem handling
* Changes to this class. What the people have been up to:
* Changes to this class. What the people have been up to: Revision 1.10 2006/11/27 09:07:52 poth
* Changes to this class. What the people have been up to: JNI integration of proj4 has been removed. The CRS functionality now will be done by native deegree code.
* Changes to this class. What the people have been up to:
* Changes to this class. What the people have been up to: Revision 1.9 2006/11/23 11:46:40 bezema
* Changes to this class. What the people have been up to: The initial version of the new wpvs
* Changes to this class. What the people have been up to: Revision 1.8
* 2006/07/05 15:59:13 poth comments corrected
*
* Revision 1.7 2006/07/05 11:24:22 taddei removed unused code
*
* Revision 1.6 2006/04/06 20:25:28 poth ** empty log message ***
*
* Revision 1.5 2006/03/30 21:20:28 poth ** empty log message ***
*
* Revision 1.4 2006/01/26 14:40:36 taddei added test method
*
* Revision 1.3 2006/01/16 20:36:39 poth ** empty log message ***
*
* Revision 1.2 2005/12/23 11:57:43 taddei added factory method
*
* Revision 1.1 2005/12/21 13:50:03 taddei first check in of old but good WTS classes
*
*
**************************************************************************************************/