package com.bg7yoz.ft8cn.maidenhead; /** * 梅登海德网格的处理。包括经纬度换算、距离计算。 * @author BGY70Z * @date 2023-03-20 */ import android.annotation.SuppressLint; import android.content.Context; import android.location.Location; import android.location.LocationManager; import com.bg7yoz.ft8cn.GeneralVariables; import com.bg7yoz.ft8cn.R; import com.google.android.gms.maps.model.LatLng; import java.util.List; public class MaidenheadGrid { private static final String TAG = "MaidenheadGrid"; private static final double EARTH_RADIUS = 6371393; // 平均半径,单位:m;不是赤道半径。赤道为6378左右 /** * 计算梅登海德网格的经纬度,4字符或6字符。如果网格数据不正确,返回null。如果是四字符的,尾部加ll,取中间的位置。 * * @param grid 梅登海德网格数据 * @return LatLng 返回经纬度,如果数据不正确,返回null */ public static LatLng gridToLatLng(String grid) { if (grid==null) return null; if (grid.length()==0) return null; //判断是不是符合梅登海德网格的规则 if (grid.length() != 2&&grid.length() != 4 && grid.length() != 6) { return null; } if (grid.equalsIgnoreCase("RR73")) return null; if (grid.equalsIgnoreCase("RR")) return null; double x=0; double y=0; double z=0; //纬度 double lat=0; if (grid.length()==2){ x=grid.toUpperCase().getBytes()[1]-'A'+0.5f; }else { x=grid.toUpperCase().getBytes()[1]-'A'; } x*=10; if (grid.length()==4){ y=grid.getBytes()[3]-'0'+0.5f; }else if (grid.length()==6){ y=grid.getBytes()[3]-'0'; } if (grid.length()==6){ z=grid.toUpperCase().getBytes()[5]-'A'+0.5f; z=z*(1/18f); } lat=x+y+z-90; //经度 x=0; y=0; z=0; double lng=0; if (grid.length()==2){ x=grid.toUpperCase().getBytes()[0]-'A'+0.5; }else { x=grid.toUpperCase().getBytes()[0]-'A'; } x*=20; if (grid.length()==4){ y=grid.getBytes()[2]-'0'+0.5; }else if (grid.length()==6){ y=grid.getBytes()[2]-'0'; } y*=2; if (grid.length()==6){ z=grid.toUpperCase().getBytes()[4]-'A'+0.5; z=z*(2/18f); } lng=x+y+z-180; if (lat>85) lat=85;//防止在地图上越界 if (lat<-85) lat=-85;//防止在地图上越界 return new LatLng(lat,lng); } public static LatLng[] gridToPolygon(String grid) { if (grid.length() != 2 && grid.length() != 4 && grid.length() != 6) { return null; } LatLng[] latLngs = new LatLng[4]; //纬度1 double x; double y = 0; double z = 0; double lat1; x = grid.toUpperCase().getBytes()[1] - 'A'; x *= 10; if (grid.length() > 2) { y = grid.getBytes()[3] - '0'; } if (grid.length() > 4) { z = grid.toUpperCase().getBytes()[5] - 'A'; z = z * (1f / 18f); } lat1 = x + y + z - 90; if (lat1<-85.0){ lat1=-85.0; } if (lat1>85.0){ lat1=85.0; } //纬度2 x = 0; y = 0; z = 0; double lat2; if (grid.length() == 2) { x = grid.toUpperCase().getBytes()[1] - 'A' + 1; } else { x = grid.toUpperCase().getBytes()[1] - 'A'; } x *= 10; if (grid.length() == 4) { y = grid.getBytes()[3] - '0' + 1; } else if (grid.length() == 6) { y = grid.getBytes()[3] - '0'; } if (grid.length() == 6) { z = grid.toUpperCase().getBytes()[5] - 'A' + 1; z = z * (1f / 18f); } lat2 = x + y + z - 90; if (lat2<-85.0){ lat2=-85.0; } if (lat2>85.0){ lat2=85.0; } //经度1 x=0;y=0;z=0; double lng1; x=grid.toUpperCase().getBytes()[0]-'A'; x*=20; if (grid.length()>2){ y=grid.getBytes()[2]-'0'; y*=2; } if (grid.length()>4){ z=grid.toUpperCase().getBytes()[4]-'A'; z=z*2/18f; } lng1=x+y+z-180; //经度2 x=0;y=0;z=0; double lng2; if (grid.length()==2){ x=grid.toUpperCase().getBytes()[0]-'A'+1; }else { x=grid.toUpperCase().getBytes()[0]-'A'; } x*=20; if (grid.length()==4){ y=grid.getBytes()[2]-'0'+1; }else if (grid.length()==6){ y=grid.getBytes()[2]-'0'; } y*=2; if (grid.length()==6){ z=grid.toUpperCase().getBytes()[4]-'A'+1; z=z*2/18f; } lng2=x+y+z-180; latLngs[0] = new LatLng(lat1,lng1); latLngs[1] = new LatLng(lat1,lng2); latLngs[2] = new LatLng(lat2,lng2); latLngs[3] = new LatLng(lat2,lng1); return latLngs; } /** * 此函数根据纬度计算 6 字符 Maidenhead网格。 * 经纬度采用 NMEA 格式。换句话说,西经和南纬度为负数。它们被指定为double类型 * * @param location 经纬度 * @return String 梅登海德字符 */ public static String getGridSquare(LatLng location) { double tempNumber;//用于中间计算 int index;//确定要显示的字符 double _long = location.longitude; double _lat = location.latitude; StringBuilder buff = new StringBuilder(); /* * 计算第一对两个字符 */ _long += 180; // 从太平洋中部开始 tempNumber = _long / 20; // 每个主要正方形都是 20 度宽 index = (int) tempNumber; // 大写字母的索引 buff.append(String.valueOf((char) (index + 'A'))); // 设置第一个字符 _long = _long - (index * 20); // 第 2 步的剩余部分 _lat += 90; //从南极开始 180 度 tempNumber = _lat / 10; // 每个大正方形高 10 度 index = (int) tempNumber; // 大写字母的索引 buff.append(String.valueOf((char) (index + 'A')));//设置第二个字符 _lat = _lat - (index * 10); // 第 2 步的剩余部分 /* * 现在是第二对两数字: */ tempNumber = _long / 2; // 步骤 1 的余数除以 2 index = (int) tempNumber; // 数字索引 buff.append(String.valueOf((char) (index + '0')));//设置第三个字符 _long = _long - (index * 2); //第 3 步的剩余部分 tempNumber = _lat; // 步骤 1 的余数除以 1 index = (int) tempNumber; // 数字索引 buff.append(String.valueOf((char) (index + '0')));//设置第四个字符 _lat = _lat - index; //第 3 步的剩余部分 /* *现在是第三对两个小写字符: */ tempNumber = _long / 0.083333; //步骤 2 的余数除以 0.083333 index = (int) tempNumber; // 小写字母的索引 buff.append(String.valueOf((char) (index + 'a')));//设置第五个字符 tempNumber = _lat / 0.0416665; // 步骤 2 的余数除以 0.0416665 index = (int) tempNumber; // 小写字母的索引 buff.append(String.valueOf((char) (index + 'a')));//设置第五个字符 return buff.toString().substring(0, 4); } /** * 计算经纬度之间的距离 * * @param latLng1 经纬度 * @param latLng2 经纬度 * @return 距离,公里。 */ public static double getDist(LatLng latLng1, LatLng latLng2) { double radiansAX = Math.toRadians(latLng1.longitude); // A经弧度 double radiansAY = Math.toRadians(latLng1.latitude); // A纬弧度 double radiansBX = Math.toRadians(latLng2.longitude); // B经弧度 double radiansBY = Math.toRadians(latLng2.latitude); // B纬弧度 // 公式中“cosβ1cosβ2cos(α1-α2)+sinβ1sinβ2”的部分,得到∠AOB的cos值 double cos = Math.cos(radiansAY) * Math.cos(radiansBY) * Math.cos(radiansAX - radiansBX) + Math.sin(radiansAY) * Math.sin(radiansBY); double acos = Math.acos(cos); // 反余弦值 return EARTH_RADIUS * acos / 1000; // 最终结果km } /** * 计算梅登海德网格之间的距离 * * @param mGrid1 梅登海德网格 * @param mGrid2 梅登海德网格2 * @return double 两个网格之间的距离 */ public static double getDist(String mGrid1, String mGrid2) { LatLng latLng1 = gridToLatLng(mGrid1); LatLng latLng2 = gridToLatLng(mGrid2); if (latLng1 != null && latLng2 != null) { return getDist(latLng1, latLng2); } else { return 0; } } /** * 计算两个网格之间的距离 * * @param mGrid1 网格 * @param mGrid2 网格 * @return 距离 */ @SuppressLint("DefaultLocale") public static String getDistStr(String mGrid1, String mGrid2) { double dist = getDist(mGrid1, mGrid2); if (dist == 0) { return ""; } else { return String.format(GeneralVariables.getStringFromResource(R.string.distance), dist); } } public static String getDistLatLngStr(LatLng latLng1,LatLng latLng2){ return String.format(GeneralVariables.getStringFromResource(R.string.distance), getDist(latLng1,latLng2)); } /** * 计算两个网格之间的距离,以英文显示公里数 * * @param mGrid1 网格 * @param mGrid2 网格 * @return 距离 */ @SuppressLint("DefaultLocale") public static String getDistStrEN(String mGrid1, String mGrid2) { double dist = getDist(mGrid1, mGrid2); if (dist == 0) { return ""; } else { return String.format("%.0f km", dist); } } /** * 获取本设备的经纬度 * * @param context context * @return 经纬度 */ public static LatLng getLocalLocation(Context context) { // 获取位置服务 String serviceName = Context.LOCATION_SERVICE; // 调用getSystemService()方法来获取LocationManager对象 LocationManager locationManager = (LocationManager) context.getSystemService(serviceName); // 指定LocationManager的定位方法 //String provider = LocationManager.GPS_PROVIDER; // 调用getLastKnownLocation()方法获取当前的位置信息 List providers = locationManager.getProviders(true); Location location = null; for (String s : providers) { @SuppressLint("MissingPermission") Location l = locationManager.getLastKnownLocation(s); if (l == null) { continue; } if (location == null || l.getAccuracy() < location.getAccuracy()) { // Found best last known location: %s", l); location = l; } } if (location != null) { return new LatLng(location.getLatitude(), location.getLongitude()); } else { return null; } } /** * 获取本机的梅登海德网格数据。需要定位的权限。 * * @param context context * @return String 返回6字符的梅登海德网格。 */ public static String getMyMaidenheadGrid(Context context) { LatLng latLng = getLocalLocation(context); if (latLng != null) { return getGridSquare(latLng); } else { //ToastMessage.show("无法定位,请确认是否有定位的权限。"); return ""; } } /** * 检查是不是梅登海德网格。如果不是返回false。 * * @param s 梅登海德网格 * @return boolean 是否是梅登海德网格。 */ public static boolean checkMaidenhead(String s) { if (s.length() != 4 && s.length() != 6) { return false; } else { if (s.equals("RR73")) { return false; } return Character.isAlphabetic(s.charAt(0)) && Character.isAlphabetic(s.charAt(1)) && Character.isDigit(s.charAt(2)) && Character.isDigit(s.charAt(3)); } } }