// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/tools/terrain/LOSGenerator.java,v $
// $RCSfile: LOSGenerator.java,v $
// $Revision: 1.6 $
// $Date: 2005/12/09 21:09:10 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.tools.terrain;
import java.awt.Color;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.bbn.openmap.MoreMath;
import com.bbn.openmap.dataAccess.dted.DTEDFrameCache;
import com.bbn.openmap.proj.GreatCircle;
import com.bbn.openmap.proj.Length;
import com.bbn.openmap.proj.Planet;
import com.bbn.openmap.proj.coords.LatLonPoint;
/**
* A Class that can do Line-Of-Sight calculations between two points. Uses the
* DTEDFrameCache to get elevations.
*/
public class LOSGenerator {
// These are used to control the algorithm type. Right now, the
// first two are eliminated, since the azimuth algorithm is
// faster
// and more precise.
final static int PRECISE = 0;
final static int GOODENOUGH = 1;
final static int AZIMUTH = 2;
// The colors of pixels
final static int DEFAULT_INVISIBLE = new Color(0, 0, 0, 0).getRGB();
final static int DEFAULT_VISIBLE = new Color(0, 255, 0, 255).getRGB();
final static int DEFAULT_MAYBEVISIBLE = new Color(255, 255, 0, 255).getRGB();
protected int INVISIBLE = DEFAULT_INVISIBLE;
protected int VISIBLE = DEFAULT_VISIBLE;
protected int MAYBEVISIBLE = DEFAULT_MAYBEVISIBLE;
DTEDFrameCache dtedCache = null;
public static Logger logger = Logger.getLogger("com.bbn.openmap.tools.terrain.LOSGenerator");
/**
* Not the preferred way to create one of these. It's full of defaults.
*/
public LOSGenerator() {
}
public LOSGenerator(DTEDFrameCache cache) {
setDtedCache(cache);
}
public void setDtedCache(DTEDFrameCache cache) {
dtedCache = cache;
}
public DTEDFrameCache getDtedCache() {
return dtedCache;
}
/**
* Check to see if two points are within line of sight of each other, taking
* into account their elevations above Mean Sea Level as retrieved by a DTED
* database, and any other addition height of each object.
*
* @param startLLP location of point 1.
* @param startObjHeight the elevation of point 1 above the surface, in
* meters. The surface elevation of the point will be looked up and
* added to this value.
* @param addStartElevation true if startObjHeight also needs DTED lookup
* added to it.
* @param endLLP location of point 2.
* @param endObjHeight the elevation of point 2 above the surface, in
* meters. The surface elevation of the point will be looked up and
* added to this value.
* @param numPoints number of sample points to check between the two end
* points. Can be dependent on the Projection of the current map, and
* based on the number of pixels between the projected points. Could
* also be based on the number of elevation posts between the two
* points in the DTED database.
* @return true of their is a line-of-sight path between the two points.
*/
public boolean isLOS(LatLonPoint startLLP, int startObjHeight, boolean addStartElevation,
LatLonPoint endLLP, int endObjHeight, int numPoints) {
boolean ret = false;
if (logger.isLoggable(Level.FINE)) {
logger.fine("LOSGenerator.isLOS: " + startLLP + " at height:" + startObjHeight + ", "
+ endLLP + " at height:" + endObjHeight + ", numPoints = " + numPoints);
}
if (dtedCache == null) {
return ret;
}
int startTotalHeight = startObjHeight
+ (addStartElevation ? dtedCache.getElevation((float) startLLP.getLatitude(), (float) startLLP.getLongitude())
: 0);
double[] llpoints = GreatCircle.greatCircle(startLLP.getRadLat(), startLLP.getRadLon(), endLLP.getRadLat(), endLLP.getRadLon(), numPoints, true);
LatLonPoint llp = new LatLonPoint.Double();
int gcPointListSize = llpoints.length;
double smallestSlopeValue = -Math.PI;
// Start at a couple of points away from origin
for (int i = 4; i < gcPointListSize; i += 2) {
llp.setLatLon(llpoints[i], llpoints[i + 1], true);
int heightAboveGround = 0;
// Only add height to end point
if (i >= gcPointListSize - 2) {
heightAboveGround = endObjHeight;
}
double slopeOfCurrentPoint = calculateLOSSlope(startLLP, startTotalHeight, llp, heightAboveGround);
// if the slope is smaller than the max slope yet seen, it is
// visible.
if (slopeOfCurrentPoint > smallestSlopeValue) {
smallestSlopeValue = slopeOfCurrentPoint;
ret = true;
// If the last point has the largest slope, then the
// point is LOS.
} else {
ret = false;
}
if (logger.isLoggable(Level.FINER)) {
logger.finer(" LOS:" + (i / 2) + " - slope = "
+ Length.DECIMAL_DEGREE.fromRadians(slopeOfCurrentPoint)
+ " at height of point: " + heightAboveGround + (ret ? " *" : " -"));
}
}
if (logger.isLoggable(Level.FINE)) {
logger.fine("LOSGenerator - points " + (ret ? "" : " NOT ") + " in LOS");
}
return ret;
}
/**
* CalculateLOSslope figures out the slope from one point to another. The
* arc_dist is in radians, and is the radian arc distance of the point from
* the center point of the image, on the earth. This slope calculation does
* take the earth's curvature into account, based on the spherical model.
* The slope returned is the angle from the end point to the beginning
* point, relative to the vertical of the end point to the center of the
* earth - i.e. starting at the axis pointing straight down into the earth,
* how many radians do you have to angle up until you hit the starting
* point. The DTED elevation of the end point is gathered from the dted
* cache.
*
* @param startLLP the coordinates of point 1.
* @param startTotalHeight the total height of point 1, from the Mean Sea
* Level - so it's the elevation of the point plus altitude above the
* surface, in meters.
* @param endLLP the coordinates of point 2.
* @param endObjHeight the elevation of point 2 above the surface, in
* meters. The surface elevation of the point will be looked up and
* added to this value.
* @return slope of line between the two points, with zero pointing straight
* down, in radians.
*/
public double calculateLOSSlope(LatLonPoint startLLP, int startTotalHeight, LatLonPoint endLLP,
int endObjHeight) {
if (dtedCache == null) {
return 0;
}
double arc_dist = startLLP.distance(endLLP);
int endTotalHeight = endObjHeight
+ dtedCache.getElevation(endLLP.getLatitude(), endLLP.getLongitude());
return calculateLOSSlope(startTotalHeight, endTotalHeight, arc_dist);
}
/**
* Calculate the slope of a line between two points across a spherical model
* of the earth. A slope of zero is pointing to the sky from the starting
* point. 90 degrees is perpendicular from that start point vector, which
* would be the slope of two points next to each other of the same height.
* Any angle greater than than and you're rolling downhill.
*
* @param startTotalHeight total height of one point, in meters. Should
* represent elevation of point which is the surface elevation above
* MSL, and the height above the surface.
* @param endTotalHeight total height of one the other point, in meters.
* Should represent elevation of point which is the surface elevation
* above MSL, and the height above the surface.
* @param arc_dist the surface angle, in radians, across the spherical model
* of the earth that separates the two points.
*/
public static double calculateLOSSlope(int startTotalHeight, int endTotalHeight, double arc_dist) {
double ret = 0;
double P = Math.sin(arc_dist) * (endTotalHeight + Planet.wgs84_earthEquatorialRadiusMeters);
double xPrime = Math.cos(arc_dist)
* (endTotalHeight + Planet.wgs84_earthEquatorialRadiusMeters);
double cutoff = startTotalHeight + Planet.wgs84_earthEquatorialRadiusMeters;
// Suggested changes, submitted by Mark Wigmore. Introduces
// use of doubles, and avoidance of PI/2 tan() calculations.
double bottom = cutoff - xPrime;
ret = MoreMath.HALF_PI_D - Math.atan(bottom / P);
return ret;
}
// public static void main(String[] args) {
//
// float dist = Length.FEET.toRadians(1000);
//
// System.out.println("slope = "
// + Length.DECIMAL_DEGREE.fromRadians(LOSGenerator.calculateLOSSlope(10,
// 10, dist)));
// System.out.println("slope = "
// + Length.DECIMAL_DEGREE.fromRadians(LOSGenerator.calculateLOSSlope(5, 10,
// dist)));
// System.out.println("slope = "
// + Length.DECIMAL_DEGREE.fromRadians(LOSGenerator.calculateLOSSlope(15,
// 10, dist)));
//
// }
}