/**
* 根据经纬度求算所在的地形图分幅编号
* 地球经度 180 + 180
* 地球纬度 90 + 90
*
* 1:100w地图是按照经度差6度,纬度差4度 将地球分为2760份
* 180 / 6 = 30
* 90 / 4 = 23
* 所以 ,100w比例的地图是将全球分成了 60 * 46 = 2760 份,按照100w的比例可以将全球分成2760份,每份大概面积18.5w平方公里
* 1:50w地图是将一副1:100w的地图四等分,2760 * 4 = 11040 份,每份大概面积4.5w平方公里
* 同理,1:25w地图是将一副1:100w的地图16等分,将全球分为 2760 * 16 = 44160 份,每份大概面积1.15w平方公里
* 1:10w地图是将一副1:100w的地图144等分,将全球分为 2760 * 144 = 397440 份,每份大概面积1283平方公里
* 1:5w地图是将一副1:10w的地图4等分,将全球分为 2760 * 144 * 4 = 1589760 份,每份大概面积320平方公里
* 1:2w5地图是将一副1:5w的地图4等分,将全球分为 2760 * 144 * 4 * 4 = 6359040 份,每份大概面积80平方公里
*/
public class MapNumberUtil {
/**
* 新版图幅规则(GB/T 13989—2012)
* 缩放比例为25000
*
* @param longitude 经度
* @param latitude 纬度
* @return 图幅编号
*/
public static String toMapNumber2W5(double longitude, double latitude) {
return toMapNumber(longitude, latitude, 25000);
}
/**
* 新版图幅规则(GB/T 13989—2012)
* 缩放比例为25000
*
* @param longitude 经度
* @param latitude 纬度
* @return 图幅编号
*/
public static String toMapNumber2W5(CoordinateDTO longitude, CoordinateDTO latitude) {
return toMapNumber(longitude, latitude, 25000);
}
/**
* 新版图幅规则(GB/T 13989—2012)
* 缩放比例为50000
*
* @param longitude 经度
* @param latitude 纬度
* @return 图幅编号
*/
public static String toMapNumber5W(double longitude, double latitude) {
return toMapNumber(longitude, latitude, 50000);
}
/**
* 新版图幅规则(GB/T 13989—2012)
* 缩放比例为50000
*
* @param longitude 经度
* @param latitude 纬度
* @return 图幅编号
*/
public static String toMapNumber5W(CoordinateDTO longitude, CoordinateDTO latitude) {
return toMapNumber(longitude, latitude, 50000);
}
/**
* 新版图幅规则(GB/T 13989—2012)
*
* @param longitude 经度
* @param latitude 纬度
* @param scaleNumber 缩放比例(1000000/500000/100000/50000),
* 推荐2w5,1:5w的比例纬度差为5分,纬度1分约等于1.85km,5分约等于9.25km
* 推荐5w,1:5w的比例纬度差为10分,纬度1分约等于1.85km,10分约等于18.5km
* @return 图幅编号
*/
public static String toMapNumber(double longitude, double latitude, Integer scaleNumber) {
return toMapNumber(toCoordinateDTO(longitude), toCoordinateDTO(latitude), scaleNumber);
}
/**
* 新版图幅规则(GB/T 13989—2012)
* 计算方法参考:https://blog.youkuaiyun.com/weixin_39716160/article/details/111815576
*
* @param longitude 经度
* @param latitude 纬度
* @param scaleNumber 缩放比例(1000000/500000/100000/50000),
* 推荐2w5,1:5w的比例纬度差为5分,纬度1分约等于1.85km,5分约等于9.25km
* 推荐5w,1:5w的比例纬度差为10分,纬度1分约等于1.85km,10分约等于18.5km
* @return 图幅编号
*/
public static String toMapNumber(CoordinateDTO longitude, CoordinateDTO latitude, Integer scaleNumber) {
// 经/纬差值计算
Double lngDiff = getLongitudeDiff(scaleNumber);
Double latDiff = getLatitudeDiff(scaleNumber);
// 度分秒转换,统一成度
Double lng = longitude.toDegreeNumber();
Double lat = latitude.toDegreeNumber();
StringBuilder sb = new StringBuilder(512);
String[] codes = toWords();
Double word_row = Double.valueOf(1) + lat / 4;
Double word_col = Double.valueOf(31) + lng / 6;
sb.append(String.format("%s%s%s", codes[word_row.intValue() - 1], word_col.intValue(), toScaleCode(scaleNumber)));
Integer number_row = Double.valueOf(4.0 / latDiff).intValue() - Double.valueOf((lat % 4.0) / latDiff).intValue();
Double number_col = lng % 6.0 / lngDiff + 1;
sb.append(String.format("%03d", number_row));
sb.append(String.format("%03d", number_col.intValue()));
return sb.toString();
}
/**
* 生成26个大写字母
*
* @return 26个大写字母数组
*/
private static String[] toWords() {
String[] codes = new String[26];
for (int i = 1; i <= 26; i++) {
codes[i - 1] = toUpperCode(i);
}
return codes;
}
/**
* 转换成单个的大写字母
*
* @param value
* @return
*/
private static String toUpperCode(Integer value) {
return String.valueOf(Character.toUpperCase((char) (96 + value)));
}
/**
* 获取缩放比例代码
*
* @param scaleNumber 缩放比例(如50000,代表1:50000)
* @return 缩放比例代码
*/
private static String toScaleCode(int scaleNumber) {
switch (scaleNumber) {
case 1000000:
return "A";
case 500000:
return "B";
case 250000:
return "C";
case 100000:
return "D";
case 50000:
return "E";
case 25000:
return "F";
case 10000:
return "G";
case 5000:
return "H";
case 2000:
return "I";
case 1000:
return "J";
case 500:
return "K";
default:
return "";
}
}
/**
* 获取经差值
*
* @param scaleNumber 缩放比例(如50000,代表1:50000)
* @return 经差值
*/
private static Double getLongitudeDiff(int scaleNumber) {
switch (scaleNumber) {
case 1000000:
return 6.0;
case 500000:
return 3.0;
case 250000:
return 1.5;
case 100000:
return 0.5;
case 50000:
return 15.0 / 60.0;
case 25000:
return 7.0 / 60.0 + 30.0 / 3600.0;
case 10000:
return 3.0 / 60.0 + 45.0 / 3600.0;
case 5000:
return 1.0 / 60.0 + 52.5 / 3600.0;
case 2000:
return 37.5 / 3600.0;
case 1000:
return 18.75 / 3600.0;
case 500:
return 9.375 / 3600.0;
default:
return 0.0;
}
}
/**
* 获取纬差值
*
* @param scaleNumber 缩放比例(如50000,代表1:50000)
* @return 纬差值
*/
private static Double getLatitudeDiff(int scaleNumber) {
switch (scaleNumber) {
case 1000000:
return 4.0;
case 500000:
return 2.0;
case 250000:
return 1.0;
case 100000:
return 20.0 / 60.0;
case 50000:
return 10.0 / 60.0;
case 25000:
return 5.0 / 60.0;
case 10000:
return 2.0 / 60.0 + 30.0 / 3600.0;
case 5000:
return 1.0 / 60.0 + 15.0 / 3600.0;
case 2000:
return 25.0 / 3600.0;
case 1000:
return 12.5 / 3600.0;
case 500:
return 6.25 / 3600.0;
default:
return 0.0;
}
}
/**
* 经纬度转换成度分秒
*
* @param longitudeOrLatitude 经纬度
* @return 度分秒
*/
public static CoordinateDTO toCoordinateDTO(double longitudeOrLatitude) {
int d = (int) longitudeOrLatitude;
int m = (int) ((longitudeOrLatitude - d) * 60);
Double degree = (double) d;
Double minute = (double) m;
Double second = ((longitudeOrLatitude - d) * 60 - m) * 60;
return new CoordinateDTO(degree, minute, second);
}
public static void main(String[] args) {
/**
* 新版图幅规则(GB/T 13989—2012)
*/
CoordinateDTO lng = toCoordinateDTO(118.32644);
CoordinateDTO lat = toCoordinateDTO(35.12542);
// 经度 纬度 缩放比例5w 缩放比例2w5
// 118.31532 35.18246 I50E005018 I50F010035
// 118.30771 35.21735 I50E005018 I50F010035
// 118.31005 35.20489 I50E005018 I50F010035
// 118.31936 35.17056 I50E005018 I50F010035
// 118.32578 35.13390 I50E006018 I50F011035
// 118.33317 35.10355 I50E006018 I50F011035
// 118.34441 35.05687 I50E006018 I50F012035
// 118.30523 35.05776 I50E006018 I50F012035
// 118.31676 35.03584 I50E006018 I50F012035
String mapNumber = MapNumberUtil.toMapNumber(lng, lat, 25000);
System.out.println(mapNumber);
}
}
import lombok.Data;
/**
* 坐标,度分秒格式
*/
@Data
public class CoordinateDTO {
/**
* ctor
*
* @param degree 度
* @param minute 分
* @param second 秒
*/
public CoordinateDTO(Double degree, Double minute, Double second) {
this.setDegree(degree);
this.setMinute(minute);
this.setSecond(second);
}
/**
* 转换成十进制的度数
*
* @return 十进制的度数
*/
public Double toDegreeNumber() {
return degree + minute / 60.0 + second / 3600.0;
}
/**
* 度
*/
private Double degree;
/**
* 分
*/
private Double minute;
/**
* 秒
*/
private Double second;
}
06-01
07-29