/* Android IMSI-Catcher Detector | (c) AIMSICD Privacy Project
* -----------------------------------------------------------
* LICENSE: http://git.io/vki47 | TERMS: http://git.io/vki4o
* -----------------------------------------------------------
*/
/**
* This util is used to calculate various points on a sphere from GPS coordinates
*
* Represents a point on the surface of a sphere. (The Earth is almost spherical.)
* To create an instance, call one of the static methods fromDegrees() or fromRadians().
*
* This code was originally published at:
* http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates#Java
*
* @author Jan Philip Matuschek
* @version 22 September 2010
*/
package com.secupwn.aimsicd.utils;
public class GeoLocation {
private double radLat; // latitude in radians
private double radLon; // longitude in radians
private double degLat; // latitude in degrees
private double degLon; // longitude in degrees
private static final double MIN_LAT = Math.toRadians(-90d); // -PI/2
private static final double MAX_LAT = Math.toRadians(90d); // PI/2
private static final double MIN_LON = Math.toRadians(-180d); // -PI
private static final double MAX_LON = Math.toRadians(180d); // PI
private GeoLocation() {
}
/**
* @param latitude the latitude, in degrees.
* @param longitude the longitude, in degrees.
*/
public static GeoLocation fromDegrees(double latitude, double longitude) {
GeoLocation result = new GeoLocation();
result.radLat = Math.toRadians(latitude);
result.radLon = Math.toRadians(longitude);
result.degLat = latitude;
result.degLon = longitude;
result.checkBounds();
return result;
}
/**
* @param latitude the latitude, in radians.
* @param longitude the longitude, in radians.
*/
public static GeoLocation fromRadians(double latitude, double longitude) {
GeoLocation result = new GeoLocation();
result.radLat = latitude;
result.radLon = longitude;
result.degLat = Math.toDegrees(latitude);
result.degLon = Math.toDegrees(longitude);
result.checkBounds();
return result;
}
private void checkBounds() {
if (radLat < MIN_LAT || radLat > MAX_LAT ||
radLon < MIN_LON || radLon > MAX_LON) {
throw new IllegalArgumentException();
}
}
/**
* @return the latitude, in degrees.
*/
public double getLatitudeInDegrees() {
return degLat;
}
/**
* @return the longitude, in degrees.
*/
public double getLongitudeInDegrees() {
return degLon;
}
/**
* @return the latitude, in radians.
*/
public double getLatitudeInRadians() {
return radLat;
}
/**
* @return the longitude, in radians.
*/
public double getLongitudeInRadians() {
return radLon;
}
@Override
public String toString() {
return "(" + degLat + "\u00B0, " + degLon + "\u00B0) = (" +
radLat + " rad, " + radLon + " rad)";
}
/**
* Computes the great circle distance between this GeoLocation instance and
* the location argument.
*
* @param radius the radius of the sphere, e.g. the average radius for a
* spherical approximation of the figure of the Earth is
* approximately 6371.01 kilometers.
*
* @return the distance, measured in the same unit as the radius argument.
*/
public double distanceTo(GeoLocation location, double radius) {
return Math.acos(Math.sin(radLat) * Math.sin(location.radLat) +
Math.cos(radLat) * Math.cos(location.radLat) *
Math.cos(radLon - location.radLon)) * radius;
}
/**
* Computes the bounding coordinates of all points on the surface of a
* sphere that have a great circle distance to the point represented by this
* GeoLocation instance that is less or equal to the distance argument.
*
*
* @param distance the distance from the point represented by this
* GeoLocation instance. Must me measured in the same
* unit as the radius argument.
*
* @param radius the radius of the sphere, e.g. the average radius for a
* spherical approximation of the figure of the Earth is
* approximately 6371.01 kilometers.
*
* @return an array of two GeoLocation objects such that:
*
* The latitude of any point within the specified distance is
* greater or equal to the latitude of the first array element and
* smaller or equal to the latitude of the second array element.
*
* If the longitude of the first array element is smaller or
* equal to the longitude of the second element, then the longitude
* of any point within the specified distance is greater or equal to
* the longitude of the first array element and smaller or equal to
* the longitude of the second array element.
*
* If the longitude of the first array element is greater than
* the longitude of the second element (this is the case if the
* 180th meridian is within the distance), then the longitude of any
* point within the specified distance is greater or equal to the
* longitude of the first array element OR smaller
* or equal to the longitude of the second array element.
*
*/
public GeoLocation[] boundingCoordinates(double distance, double radius) {
if (radius < 0d || distance < 0d) {
throw new IllegalArgumentException();
}
// angular distance in radians on a great circle
double radDist = distance / radius;
double minLat = radLat - radDist;
double maxLat = radLat + radDist;
double minLon, maxLon;
if (minLat > MIN_LAT && maxLat < MAX_LAT) {
double deltaLon = Math.asin(Math.sin(radDist) /
Math.cos(radLat));
minLon = radLon - deltaLon;
if (minLon < MIN_LON) {
minLon += 2d * Math.PI;
}
maxLon = radLon + deltaLon;
if (maxLon > MAX_LON) {
maxLon -= 2d * Math.PI;
}
} else {
// a pole is within the distance
minLat = Math.max(minLat, MIN_LAT);
maxLat = Math.min(maxLat, MAX_LAT);
minLon = MIN_LON;
maxLon = MAX_LON;
}
return new GeoLocation[]{
fromRadians(minLat, minLon),
fromRadians(maxLat, maxLon)
};
}
}