// Created by plusminus on 23:51:49 - 24.02.2008
package org.androad.nav.util;
import org.osmdroid.util.GeoPoint;
import org.androad.adt.other.GraphicsPoint;
import org.androad.util.constants.GeoConstants;
import org.androad.util.constants.MathematicalConstants;
import android.graphics.Point;
import android.location.Location;
import android.util.FloatMath;
public class Util implements MathematicalConstants, GeoConstants{
// ===========================================================
// Final Fields
// ===========================================================
// ===========================================================
// Methods
// ===========================================================
public static boolean inBounds(final int lower, final int val, final int upper) {
return lower <= val && upper >= val;
}
/**
* Calculates the distance from a line spanned between two MapPoints to another MapPoint.
* This is only the geometric distance,
* when the search-point is located like:
* P or Q, but not like X,Y or Z:
* <pre>
* ^
* | Y | P |
* | | |
* | A-----B
* | | | Z
* | X | Q |
* |
* 0-----------------></pre>
* In cases of X and Y, the distance to A would be returned.
* In the case of Z, the distance to B would be returned.
* @param linePointA First (Map)Point on the line
* @param linePointB Second (Map)Point on the line
* @param p Point to determine the distance to the line
* @return distance from a Point to a line
*/
public static float getDistanceToLine(final GeoPoint linePointA, final GeoPoint linePointB, final Point p){
/* a: Point A on the line. */
final Point a = new Point(linePointA.getLongitudeE6(), linePointA.getLatitudeE6());
/* b: Point B on the line. */
final Point b = new Point(linePointB.getLongitudeE6(), linePointB.getLatitudeE6());
return getDistanceToLine(a, b, p);
}
public static float getDistanceToLine(final Point a, final Point b, final Point p){
/* If a is the same point as b then return the distance from p to a. */
if(a.x == b.x && a.y ==b.y){
final int dx = p.x - a.x;
final int dy = p.y - a.y;
return FloatMath.sqrt(dx*dx + dy*dy);
}
/* s is the vector from a to b. */
final Point s = org.androad.adt.other.GraphicsPoint.difference(b, a);
final float lenght_s = FloatMath.sqrt(((long)s.x) * s.x + ((long)s.y) * s.y);
/* r is the vector from a to p. */
final Point r = org.androad.adt.other.GraphicsPoint.difference(p, a);
/* The case when the angle at a is 'overstretched' */
/* Determine the angle between s and r. */
final double angleAtA = Math.acos(org.androad.adt.other.GraphicsPoint.dotProduct(r, s) / (lenght_s * FloatMath.sqrt(((long)r.x) * r.x + ((long)r.y) * r.y)));
/* If it is bigger than |90°| return distance from p to a*/
if(Math.abs(angleAtA) > PI_HALF){
final int dx = p.x - a.x;
final int dy = p.y - a.y;
return FloatMath.sqrt(dx*dx + dy*dy);
}
/* Attention: s now points to the other direction! */
s.negate();
/* t is the vector from b to p. */
final Point t = GraphicsPoint.difference(p, b);
/* The case when the angle at b is 'overstretched' */
/* Determine the angle between s and r. */
final double angleAtB = Math.acos(GraphicsPoint.dotProduct(s, t) / (lenght_s * FloatMath.sqrt(((long)t.x * t.x) + ((long)t.y) * t.y)));
/* If it is bigger than |90°| return distance from p to a*/
if(Math.abs(angleAtB) > PI_HALF){
final int dx = p.x - b.x;
final int dy = p.y - b.y;
return FloatMath.sqrt(dx*dx + dy*dy);
}
/* Check if Point is exactly on the line. */
if(Double.isNaN(angleAtA)){
// || Double.isNaN(angleAtB) // NOTE: Not needed because angleAtA would also be NaN !
return 0.0f;
}
/* Calculate the geometric distance.
* |(p-a) x b| / |b| */
return Math.abs(GraphicsPoint.crossProduct(GraphicsPoint.difference(p, a), s)) / FloatMath.sqrt(((long)s.x * s.x) + ((long)s.y) * s.y);
}
/**
* @param linePointA
* @param linePointB
* @param p
* @return the projected MapPoint if possible. <br/>
* <ul>
* <li><code>null</code> if no projection is possible</li>
* <li>when project ON the line was not possible.</li>
* <li>when a == b</li>
* </ul>.
*/
public static GeoPoint getProjectedGeoPoint(final GeoPoint linePointA, final GeoPoint linePointB, final Point p){
/* a: Point A on the line. */
final Point a = new Point(linePointA.getLongitudeE6(), linePointA.getLatitudeE6()); /* Longitude is X, Latitude is Y. */
/* b: Point B on the line. */
final Point b = new Point(linePointB.getLongitudeE6(), linePointB.getLatitudeE6()); /* Longitude is X, Latitude is Y. */
/* If a is the same point as b then return null. */
if(a.x == b.x && a.y ==b.y) {
return null;
}
/* s is the vector from a to b. */
final Point s = GraphicsPoint.difference(b, a);
final float lenght_s = FloatMath.sqrt(((long)s.x) * s.x + ((long)s.y) * s.y);
/* r is the vector from a to p. */
final Point r = GraphicsPoint.difference(p, a);
/* The case when the angle at a is 'overstretched' */
/* Determine the angle between s and r. */
final double angleAtA = Math.acos(GraphicsPoint.dotProduct(r, s) / (lenght_s * FloatMath.sqrt(((long)r.x) * r.x + ((long)r.y) * r.y)));
/* If it is bigger than |90°| return null. */
if(Math.abs(angleAtA) > PI_HALF) {
return null;
} else if(Double.isNaN(angleAtA)) {
return new GeoPoint(p.y,p.x); /* MapPoint is defined as Latitude(Y),Longitude(X). */
}
/* Attention: s now points to the other direction! */
s.negate();
/* t is the vector from b to p. */
final Point t = GraphicsPoint.difference(p, b);
/* The case when the angle at b is 'overstretched' */
/* Determine the angle between s and r. */
final float angleAtB = (float)Math.acos(GraphicsPoint.dotProduct(s, t) / (lenght_s * FloatMath.sqrt(((long)t.x) * t.x + ((long)t.y) * t.y)));
/* If it is bigger than |90°| return b*/
if(Math.abs(angleAtB) > PI_HALF) {
return null;
// NOTE: Not needed because angleAtA would also be NaN !
// else if(Double.isNaN(angleAtB))
// return new MapPoint(p.x,p.y);
}
/* Attention: s now points back to the original direction! */
s.negate();
/* Do the actual projection */
/* First: Calculate the geometric distance.
* |(p-a) x b| / |b| */
final float distance = GraphicsPoint.crossProduct(GraphicsPoint.difference(p, a), s) / FloatMath.sqrt(((long)s.x * s.x) + ((long)s.y) * s.y);
/* Calculate the gradient of the line from a to be (what already is 's'). */
final float angleOfOrthogonalRad = (float)Math.atan2(- s.x, s.y);
/* NOTE: MapPoint is defined as Latitude(Y),Longitude(X). */
return new GeoPoint(p.y - Math.round(distance * FloatMath.sin(angleOfOrthogonalRad)),
p.x - Math.round(distance * (float)Math.cos(angleOfOrthogonalRad)));
}
public static Point geoPoint2Point(final GeoPoint aGP) {
return new Point(aGP.getLongitudeE6(), aGP.getLatitudeE6());
}
/** Converts an {@link Location} to an {@link Point}. */
public static Point location2Point(final Location aLocation){
return new Point((int) (aLocation.getLongitude() * 1E6),
(int) (aLocation.getLatitude() * 1E6));
}
/** Converts an {@link Location} to a {@link GeoPoint}. */
public static GeoPoint location2GeoPoint(final Location aLocation){
return new GeoPoint((int) (aLocation.getLatitude() * 1E6),
(int) (aLocation.getLongitude() * 1E6));
}
/**
* Calculates the bearing of the two Locations supplied and returns the
* Angle in the following (GPS-likely) manner: <br />
* <code>N:0°, E:90°, S:180°, W:270°</code>
*/
public static float calculateBearing(final GeoPoint before, final GeoPoint after) {
final Point pBefore = geoPoint2Point(before);
final Point pAfter = geoPoint2Point(after);
final float res = -(float) (Math.atan2(pAfter.y - pBefore.y, pAfter.x
- pBefore.x) * 180 / PI) + 90.0f;
if (res < 0) {
return res + 360.0f;
} else {
return res;
}
}
/**
* Calculates the bearing of the two Locations supplied and returns the
* Angle in the following (GPS-likely) manner: <br />
* <code>N:0°, E:90°, S:180°, W:270°</code>
*/
public static float calculateBearing(final Location before, final Location after) {
final Point pBefore = location2Point(before);
final Point pAfter = location2Point(after);
final float res = -(float) (Math.atan2(pAfter.y - pBefore.y, pAfter.x
- pBefore.x) * 180 / PI) + 90.0f;
if (res < 0) {
return res + 360.0f;
} else {
return res;
}
}
/**
* Calculates the bearing of the two Locations supplied and returns the
* Angle in mathematical manner: <br />
* <code>Right: 0° ; Up: 90°; Left: 180°, Down: 270°</code>
*/
public static float calculateBearing(final Point coordsA, final Point coordsB) {
// !!!!!! UNTESTED !!!!!!
final float res = (float) (Math.atan2(coordsA.y - coordsB.y, coordsA.x - coordsB.x) * 180 / PI);
if (res < 0) {
return res + 360.0f;
} else {
return res;
}
}
/**
* @deprecated Method is probably not correct !!!
* @param geoPointA
* @param geoPointB
* @return
*/
@Deprecated
public static int distanceSquared(final GeoPoint geoPointA, final GeoPoint geoPointB) {
final int dx = geoPointA.getLongitudeE6() - geoPointB.getLongitudeE6();
final int dy = geoPointA.getLatitudeE6() - geoPointB.getLatitudeE6();
return dx*dx + dy*dy;
}
}