content: /* 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 zz.aimsicd.lite.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)
          };
      }
  
  }
  