/**
  * @Description: 各地图API坐标系统比较与转换; WGS84坐标系:即地球坐标系,国际上通用的坐标系。设备一般包含GPS芯片或者北斗芯片获取的经纬度为WGS84地理坐标系,
  *               谷歌地图采用的是WGS84地理坐标系(中国范围除外);
  *               GCJ02坐标系:即火星坐标系,是由中国国家测绘局制订的地理信息系统的坐标系统。由WGS84坐标系经加密后的坐标系。
  *               谷歌中国地图和搜搜中国地图采用的是GCJ02地理坐标系; BD09坐标系:即百度坐标系,GCJ02坐标系经加密后的坐标系;
  *               搜狗坐标系、图吧坐标系等,估计也是在GCJ02基础上加密而成的。
  */
 public class GpsUtil {
   public static final String BAIDU_LBS_TYPE = "bd09ll";  public final static double PI = 3.1415926535897932384626;
   public final static double X_PI = PI * 3000.0 / 180.0;  /**
    * 地球赤道半径
    */
   public final static double GLOBAL_R = 6371393.0;  public final static double EE = 0.00669342162296594323;
  /**
    * 国际坐标 to 火星坐标系 (GCJ-02) World Geodetic System ==> Mars Geodetic System
    *
    * @param wgsLat 纬度
    * @param wgsLgt 经度
    * @return GpsTranslateDTO
    */
   public static GpsTranslateDTO wgs84ToGcj02(double wgsLat, double wgsLgt) {
     if (outOfChina(wgsLat, wgsLgt)) {
       return null;
     }
     double dLat = transformLat(wgsLgt - 105.0, wgsLat - 35.0);
     double dLon = transformLon(wgsLgt - 105.0, wgsLat - 35.0);
     double radLat = wgsLat / 180.0 * PI;
     double magic = Math.sin(radLat);
     magic = 1 - EE * magic * magic;
     double sqrtMagic = Math.sqrt(magic);
     dLat = (dLat * 180.0) / ((GLOBAL_R * (1 - EE)) / (magic * sqrtMagic) * PI);
     dLon = (dLon * 180.0) / (GLOBAL_R / sqrtMagic * Math.cos(radLat) * PI);
     double mgLat = wgsLat + dLat;
     double mgLon = wgsLgt + dLon;
     return new GpsTranslateDTO(mgLat, mgLon);
   }  /**
    * dddmmmmmm格式国际坐标 to 火星坐标系 (GCJ-02)
    *
    * @param wgsLat 纬度
    * @param wgsLgt 经度
    * @return GpsTranslateDTO
    */
   public static GpsTranslateDTO wgs84ToGcj02(int wgsLat, int wgsLgt) {
     return wgs84ToGcj02(changeRad(wgsLat), changeRad(wgsLgt));
   }  /**
    * * 火星坐标系 (GCJ-02) to 国际坐标
    *
    * @param ggLat 纬度
    * @param ggLgt 经度
    * @return GpsTranslateDTO
    */
   public static GpsTranslateDTO gcj02ToWgs84(double ggLat, double ggLgt) {
     GpsTranslateDTO gps = transform(ggLat, ggLgt);
     double wgsLgt = ggLgt * 2 - gps.getLgt();
     double wgsLat = ggLat * 2 - gps.getLat();
     return new GpsTranslateDTO(wgsLat, wgsLgt);
   }  /**
    * 火星坐标系 (GCJ-02) to 百度坐标系 (BD-09) 的转换算法 将 GCJ-02 坐标转换成 BD-09 坐标
    *
    * @param ggLat 纬度
    * @param ggLgt 经度
    * @return GpsTranslateDTO
    */
   public static GpsTranslateDTO gcj02ToBd09(double ggLat, double ggLgt) {
     double z = Math.sqrt(ggLgt * ggLgt + ggLat * ggLat) + 0.00002 * Math.sin(ggLat * X_PI);
     double theta = Math.atan2(ggLat, ggLgt) + 0.000003 * Math.cos(ggLgt * X_PI);
     double bdLgt = z * Math.cos(theta) + 0.0065;
     double bdLat = z * Math.sin(theta) + 0.006;
     return new GpsTranslateDTO(bdLat, bdLgt);
   }  /**
    * * 百度坐标系 (BD-09) to 火星坐标系 (GCJ-02) 的转换算法 * * 将 BD-09 坐标转换成GCJ-02 坐标
    *
    * @param bdLat 纬度
    * @param bdLgt 经度
    * @return GpsTranslateDTO
    */
   public static GpsTranslateDTO bd09ToGcj02(double bdLat, double bdLgt) {
     double x = bdLgt - 0.0065, y = bdLat - 0.006;
     double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * X_PI);
     double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * X_PI);
     double ggLgt = z * Math.cos(theta);
     double ggLat = z * Math.sin(theta);
     return new GpsTranslateDTO(ggLat, ggLgt);
   }  /**
    * 百度坐标系 (BD-09) to 国际坐标
    *
    * @param bdLat 纬度
    * @param bdLgt 经度
    * @return GpsTranslateDTO
    */
   public static GpsTranslateDTO bd09ToWgs84(double bdLat, double bdLgt) {
     GpsTranslateDTO gcj02 = bd09ToGcj02(bdLat, bdLgt);
     return gcj02ToWgs84(gcj02.getLat(), gcj02.getLgt());
   }  /**
    * 国际坐标 to 百度坐标系 (BD-09)
    *
    * @param wgsLat 纬度
    * @param wgsLgt 经度
    * @return GpsTranslateDTO
    */
   public static GpsTranslateDTO wgs84ToBd09(double wgsLat, double wgsLgt) {
     GpsTranslateDTO gcj02 = wgs84ToGcj02(wgsLat, wgsLgt);
     if (null == gcj02) {
       return new GpsTranslateDTO(0.0, 0.0);
     }
     return gcj02ToBd09(gcj02.getLat(), gcj02.getLgt());
   }  /**
    * dddmmmmmm格式国际坐标 to 百度坐标系 (BD-09)
    *
    * @param wgsLat 纬度
    * @param wgsLgt 经度
    * @return
    */
   public static GpsTranslateDTO wgs84ToBd09(int wgsLat, int wgsLgt) {
     return wgs84ToBd09(changeRad(wgsLat), changeRad(wgsLgt));
   }  private static boolean outOfChina(double lat, double lon) {
     return lon < 72.004 || lon > 137.8347 || lat < 0.8293 || lat > 55.8271;
   }  private static GpsTranslateDTO transform(double lat, double lon) {
     if (outOfChina(lat, lon)) {
       return new GpsTranslateDTO(lat, lon);
     }
     double dLat = transformLat(lon - 105.0, lat - 35.0);
     double dLon = transformLon(lon - 105.0, lat - 35.0);
     double radLat = lat / 180.0 * PI;
     double magic = Math.sin(radLat);
     magic = 1 - EE * magic * magic;
     double sqrtMagic = Math.sqrt(magic);
     dLat = (dLat * 180.0) / ((GLOBAL_R * (1 - EE)) / (magic * sqrtMagic) * PI);
     dLon = (dLon * 180.0) / (GLOBAL_R / sqrtMagic * Math.cos(radLat) * PI);
     double mgLat = lat + dLat;
     double mgLon = lon + dLon;
     return new GpsTranslateDTO(mgLat, mgLon);
   }  private static double transformLat(double x, double y) {
     double ret =
         -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
     ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
     ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0;
     ret += (160.0 * Math.sin(y / 12.0 * PI) + 320 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0;
     return ret;
   }  private static double transformLon(double x, double y) {
     double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
     ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
     ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0;
     ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0;
     return ret;
   }  /**
    * 将dddmmmmmm或者ddmmmmmm转换为十进制的度
    *
    * @param val dddmmmmmm表示的经纬度
    * @return 转化后的十进制经纬度
    */
   private static double changeRad(int val) {    if (val >= 0) {
      return Math.round((val / 1000000 + (val - (val / 1000000) * 1000000) / 600000.0) * 100000.0)
           / 100000.0;
     } else {
       val = -val;
       return -Math.round((val / 1000000 + (val - (val / 1000000) * 1000000) / 600000.0) * 100000.0)
           / 100000.0;
     }  }
  public static double calculateAutoSpeed(GpsDataDTO gps1, GpsDataDTO gps2) {
     double distance = calculateDistance(gps1.getLng(), gps1.getLat(), gps2.getLng(), gps2.getLat(),
         EcallConst.MILE);
     return (distance / Math.abs(TimeTransform.dateStrToUnixTimeStamp(gps1.getDate())
         - TimeTransform.dateStrToUnixTimeStamp(gps2.getDate()))) * 3.6;
   }  public static double calculateAutoSpeed(SourceGps gps1, SourceGps gps2) {
     double distance =
         calculateDistance(gps1.getX(), gps1.getY(), gps2.getX(), gps2.getY(), EcallConst.MILE);
     return (distance / Math.abs(gps1.getT() - gps2.getT())) * 3.6;
   }  /**
    * TODO 计算地球上两点之间的距离
    *
    * @param lat1
    * @param lng1
    * @param lat2
    * @param lng2
    * @return Create at 2017年8月10日 下午1:08:39
    */
   public static double calculateDistance(double lng1, double lat1, double lng2, double lat2,
       String mileUnit) {
     double radLat1 = rad(lat1);
     double radLat2 = rad(lat2);
     // 纬度差
     double radLatSpan = radLat1 - radLat2;
     // 经度差
     double radLngSpan = rad(lng1) - rad(lng2);    double dis = Math.pow(Math.sin(radLatSpan / 2), 2)
         + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(radLngSpan / 2), 2);    dis = 2 * Math.asin(Math.min(1, Math.sqrt(dis))) * EcallConst.EARTH_RADIUS_CENTER;
    switch (mileUnit) {
       // 千米
       case EcallConst.KMILE:
         return MathUtil.round(dis, 3);
       // 米
       case EcallConst.MILE:
         return MathUtil.round(dis * 1000, 3);
       // 厘米
       case EcallConst.CMILE:
         return MathUtil.round(dis * 100000, 3);
       default:
         // 默认返回千米
         return MathUtil.round(dis, 3);
     }
   }  private static double rad(double d) {
     return d * Math.PI / 180.0;
   }  public static JSONObject gpsJsonFromPojo(double[] xy, double hight, long time) {
     JSONObject ob = new JSONObject();
     ob.put(PojoFieldConst.GPS_LONGITUDE, MathUtil.round(xy[0], 6));
     ob.put(PojoFieldConst.GPS_LATITUDE, MathUtil.round(xy[1], 6));
     ob.put(PojoFieldConst.GPS_ALTITUDE, hight);
     ob.put(PojoFieldConst.GPS_DATE, TimeTransform.unixTimeStampToDateStr(time));
     return ob;
   }  public static JSONObject gpsJsonFromPojo(double[] xy, double hight, String date) {
     JSONObject ob = new JSONObject();
     ob.put(PojoFieldConst.GPS_LONGITUDE, MathUtil.round(xy[0], 6));
     ob.put(PojoFieldConst.GPS_LATITUDE, MathUtil.round(xy[1], 6));
     ob.put(PojoFieldConst.GPS_ALTITUDE, hight);
     ob.put(PojoFieldConst.GPS_DATE, date);
     return ob;
   }  public static double su(double num) {
     BigDecimal bg = new BigDecimal(num);
     return bg.setScale(6, BigDecimal.ROUND_HALF_UP).doubleValue();
   }   public static void main(String[] args) {
     GpsTranslateDTO wgs84ToBd09 = wgs84ToBd09(经度, 维度);
     System.out.println(su(wgs84ToBd09.getLat()));
     System.out.println(su(wgs84ToBd09.getLgt()));
   }
 }
  @Setter
 @Getter
 @NoArgsConstructor
 @AllArgsConstructor
 @ToString
 public class GpsTranslateDTO implements Serializable {    private static final long serialVersionUID = 5717495677928773775L;
    private Double lat;
     private Double lgt;}
 
/**
  */
 public class MathUtil {
     // 默认除法运算精度
     private static final int DEF_DIV_SCALE = 10;    /**
      * 提供精确的加法运算。
      * 
      * @param v1
      *            被加数
      * @param v2
      *            加数
      * @return 两个参数的和
      */    public static double add(double v1, double v2) {
         BigDecimal b1 = new BigDecimal(v1);
         BigDecimal b2 = new BigDecimal(v2);
         return b1.add(b2).doubleValue();
     }    /**
      * 提供精确的减法运算。
      * 
      * @param v1
      *            被减数
      * @param v2
      *            减数
      * @return 两个参数的差
      */    public static double sub(double v1, double v2) {
         BigDecimal b1 = new BigDecimal(v1);
         BigDecimal b2 = new BigDecimal(v2);
         return b1.subtract(b2).doubleValue();
     }    /**
      * 提供精确的乘法运算。
      * 
      * @param v1
      *            被乘数
      * @param v2
      *            乘数
      * @return 两个参数的积
      */    public static double mul(double v1, double v2) {
         BigDecimal b1 = new BigDecimal(v1);
         BigDecimal b2 = new BigDecimal(v2);
         return b1.multiply(b2).doubleValue();
     }    /**
      * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入。
      * 
      * @param v1
      *            被除数
      * @param v2
      *            除数
      * @return 两个参数的商
      */    public static double div(double v1, double v2) {
         return div(v1, v2, DEF_DIV_SCALE);
     }    /**
      * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。
      * 
      * @param v1
      *            被除数
      * @param v2
      *            除数
      * @param scale
      *            表示表示需要精确到小数点以后几位。
      * @return 两个参数的商
      */    public static double div(double v1, double v2, int scale) {
         if (scale < 0) {
             throw new IllegalArgumentException(
                     "The scale must be a positive integer or zero");
         }
         BigDecimal b1 = new BigDecimal(v1);
         BigDecimal b2 = new BigDecimal(v2);
         return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
     }    /**
      * 提供精确的小数位四舍五入处理。
      * 
      * @param val
      *            需要四舍五入的数字
      * @param scale
      *            小数点后保留几位
      * @return 四舍五入后的结果
      */
     public static double round(double val, int scale) {
         if (scale < 0) {
             throw new IllegalArgumentException(
                     "The scale must be a positive integer or zero");
         }
         BigDecimal b = new BigDecimal(val);
         BigDecimal one = new BigDecimal(1.0);
         return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
     }
 }