使用Java实现的新版地图图幅编号计算公式(新版图幅规则(GB/T 13989—2012))
前言
最近项目需求,需要根据经纬度,计算出图幅编号。本来想着找轮子,结果找了一大圈没找到,就自己找到相关书籍,看计算公式,重复造了一个轮子。
比如根据,北纬:30°56′50″,东经:105°4′25″,计算新旧图幅编号,根据公式,我们按照1:50000比例,可以计算出,新版图幅编号为:H48E007013,旧版图幅编号为:8-48-43-A
新版本的计算公式,以及通过新版本的图幅编号,计算出旧图幅编号
1 package com.jucheap.utils; 2 3 import com.jucheap.base.CoordinateDTO; 4 5 /** 6 * <p> 7 * 地图图幅编号计算 8 * </p> 9 * 10 * @Author jucheap 11 * @Email jucheap@jucheap.com 12 * @Date 2020/8/22 13 */ 14 public class MapNumberUtil { 15 16 /** 17 * 新版图幅规则(GB/T 13989—2012) 18 * @param longitude 经度 19 * @param latitude 纬度 20 * @param scaleNumber 缩放比例(1000000/500000/100000/50000) 21 * @return 图幅编号 22 */ 23 public static String toMapNumber(CoordinateDTO longitude, CoordinateDTO latitude, Integer scaleNumber) { 24 // 经/纬差值计算 25 Double lngDiff = getLongitudeDiff(scaleNumber); 26 Double latDiff = getLatitudeDiff(scaleNumber); 27 // 度分秒转换,统一成度 28 Double lng = longitude.toDegreeNumber(); 29 Double lat = latitude.toDegreeNumber(); 30 StringBuilder sb = new StringBuilder(512); 31 String[] codes = toWords(); 32 Double word_row = Double.valueOf(1) + lat / 4; 33 Double word_col = Double.valueOf(31) + lng / 6; 34 sb.append(String.format("%s%s%s", codes[word_row.intValue() - 1], word_col.intValue(), toScaleCode(scaleNumber))); 35 Integer number_row = Double.valueOf(4.0 / latDiff).intValue() - Double.valueOf((lat % 4.0) / latDiff).intValue(); 36 Double number_col = lng % 6.0 / lngDiff + 1; 37 sb.append(String.format("%03d", number_row)); 38 sb.append(String.format("%03d", number_col.intValue())); 39 return sb.toString(); 40 } 41 42 /** 43 * 生成26个大写字母 44 * @return 26个大写字母数组 45 */ 46 private static String[] toWords() { 47 String[] codes = new String[26]; 48 for (int i = 1; i <= 26; i++) { 49 codes[i - 1] = toUpperCode(i); 50 } 51 return codes; 52 } 53 54 /** 55 * 转换成单个的大写字母 56 * @param value 57 * @return 58 */ 59 private static String toUpperCode(Integer value) { 60 return String.valueOf(Character.toUpperCase((char) (96 + value))); 61 } 62 63 /** 64 * 获取缩放比例代码 65 * 66 * @param scaleNumber 缩放比例(如50000,代表1:50000) 67 * @return 缩放比例代码 68 */ 69 private static String toScaleCode(int scaleNumber) { 70 switch (scaleNumber) { 71 case 500000: 72 return "B"; 73 case 250000: 74 return "C"; 75 case 100000: 76 return "D"; 77 case 50000: 78 return "E"; 79 case 25000: 80 return "F"; 81 case 10000: 82 return "G"; 83 case 5000: 84 return "H"; 85 case 2000: 86 return "I"; 87 case 1000: 88 return "J"; 89 case 500: 90 return "K"; 91 default: 92 return ""; 93 } 94 } 95 96 /** 97 * 获取经差值 98 * 99 * @param scaleNumber 缩放比例(如50000,代表1:50000) 100 * @return 经差值 101 */ 102 private static Double getLongitudeDiff(int scaleNumber) { 103 switch (scaleNumber) { 104 case 1000000: 105 return 6.0; 106 case 500000: 107 return 3.0; 108 case 250000: 109 return 1.5; 110 case 100000: 111 return 0.5; 112 case 50000: 113 return 15.0 / 60.0; 114 case 25000: 115 return 7.0 / 60.0 + 30.0 / 3600.0; 116 case 10000: 117 return 3.0 / 60.0 + 45.0 / 3600.0; 118 case 5000: 119 return 1.0 / 60.0 + 52.5 / 3600.0; 120 case 2000: 121 return 37.5 / 3600.0; 122 case 1000: 123 return 18.75 / 3600.0; 124 case 500: 125 return 9.375 / 3600.0; 126 default: 127 return 0.0; 128 } 129 } 130 131 /** 132 * 获取纬差值 133 * 134 * @param scaleNumber 缩放比例(如50000,代表1:50000) 135 * @return 纬差值 136 */ 137 private static Double getLatitudeDiff(int scaleNumber) { 138 switch (scaleNumber) { 139 case 1000000: 140 return 4.0; 141 case 500000: 142 return 2.0; 143 case 250000: 144 return 1.0; 145 case 100000: 146 return 20.0 / 60.0; 147 case 50000: 148 return 10.0 / 60.0; 149 case 25000: 150 return 5.0 / 60.0; 151 case 10000: 152 return 2.0 / 60.0 + 30.0 / 3600.0; 153 case 5000: 154 return 1.0 / 60.0 + 15.0 / 3600.0; 155 case 2000: 156 return 25.0 / 3600.0; 157 case 1000: 158 return 12.5 / 3600.0; 159 case 500: 160 return 6.25 / 3600.0; 161 default: 162 return 0.0; 163 } 164 } 165 166 /** 167 * 新的图幅编号转换成旧的图幅编号 168 * @param newMapNumber 新图幅编号 169 * @return 旧图幅编号 170 */ 171 public static String toOldMapNumber(String newMapNumber) { 172 if (newMapNumber.length() != 3 && newMapNumber.length() != 10) { 173 return ""; 174 } 175 char[] newMapNumberChars = newMapNumber.toCharArray(); 176 String bigRowNo = String.valueOf(newMapNumberChars[0] - 'A' + 1); 177 String bigColNo = newMapNumber.substring(1, 3); 178 if (newMapNumber.length() == 3) { //100W 179 return String.format("%s-%s", bigRowNo, bigColNo); 180 } else { 181 // 新图幅号的行列代码,col_number为行代码,col_number为列代码 182 Integer row_number = Integer.valueOf(newMapNumber.substring(4, 7)); 183 Integer col_number = Integer.valueOf(newMapNumber.substring(7, 10)); 184 // 提取行代码和列代码,若非数字,则表示图幅号格式不正确 185 char mapScaleNumber = newMapNumberChars[3]; 186 if (mapScaleNumber == 'B') { //50W 187 //region 50W 188 if (row_number > 2 || col_number > 2){ 189 return ""; 190 } 191 //旧图幅号对应的地图代码 192 int X = (row_number - 1) * 2 + (col_number - 1) + 1; 193 return String.format("%s-%s-%s", bigRowNo, bigColNo, toUpperCode(X)); 194 //endregion 195 } else if (mapScaleNumber == 'C') {//25W 196 //region 25W 197 if (row_number > 4 || col_number > 4) { 198 return ""; 199 } 200 //旧图幅号对应的地图代码 201 int X = (row_number - 1) * 4 + (col_number - 1) + 1; 202 // String code = String.format("%02d", X); 203 String code = String.valueOf(X); 204 return String.format("%s-%s-[%s]", bigRowNo, bigColNo, code); 205 //endregion 206 } else if (mapScaleNumber == 'D') {//10W 207 //region 10W 208 if (row_number > 12 || col_number > 12){ 209 return ""; 210 } 211 //旧图幅号对应的地图代码 212 int X = (row_number - 1) * 12 + (col_number - 1) + 1; 213 //String code = String.format("%03d", X); 214 String code = String.valueOf(X); 215 return String.format("%s-%s-%s", bigRowNo, bigColNo, code); 216 //endregion 217 } else if (mapScaleNumber == 'E') { //5W 218 //region 5W 219 if (row_number > 24 || col_number > 24) { 220 return ""; 221 } 222 //10W地形图对应的行号 223 int H10 = (row_number - 1) / 2 + 1; 224 //10W地形图对应的列号 225 int L10 = (col_number - 1) / 2 + 1; 226 //10W旧图幅号对应的地图代码 227 int X10 = (H10 - 1) * 12 + (L10 - 1) + 1; 228 //String code = String.format("%03d", X10); 229 String code = String.valueOf(X10); 230 //旧图幅号对应的地图代码 231 int X = (row_number - 2 * H10 + 1) * 2 + (col_number - 2 * L10 + 1) + 1; 232 return String.format("%s-%s-%s-%s", bigRowNo, bigColNo, code, toUpperCode(X)); 233 //endregion 234 } else if (mapScaleNumber == 'F') {236 //5W地形图对应的行号 237 int H5 = (row_number - 1) / 2 + 1; 238 //5W地形图对应的列号 239 int L5 = (col_number - 1) / 2 + 1; 240 241 //10W地形图对应的行号 242 int H10 = (H5 - 1) / 2 + 1; 243 //10W地形图对应的列号 244 int L10 = (L5 - 1) / 2 + 1; 245 246 Integer X2 = (row_number - 2 * H5 + 1) * 2 + (col_number - 2 * L5 + 1) + 1; 247 Integer X5 = (H5 - 2 * H10 + 1) * 2 + (L5 -2 * L10 + 1) + 1; 248 Integer X10 = (H10 - 1) * 12 + (L10 - 1) + 1; 249 250 return String.format("%s-%s-%s-%s-%s", bigRowNo, bigColNo, X10, toUpperCode(X5), X2); 251 } 252 //图幅号格式不正确 253 return ""; 254 } 255 } 256 }
坐标模型类代码
1 package com.jucheap.base; 2 3 import lombok.Getter; 4 import lombok.Setter; 5 6 /** 7 * <p> 8 * 地图坐标对象 9 * </p> 10 * 11 * @Author jucheap 12 * @Email jucheap@jucheap.com 13 * @Date 2020/8/22 14 */ 15 16 @Getter 17 @Setter 18 public class CoordinateDTO { 19 20 /** 21 * ctor 22 * @param degree 度 23 * @param minute 分 24 * @param second 秒 25 */ 26 public CoordinateDTO(Double degree, Double minute, Double second) { 27 this.setDegree(degree); 28 this.setMinute(minute); 29 this.setSecond(second); 30 } 31 32 /** 33 * 转换成十进制的度数 34 * 35 * @return 十进制的度数 36 */ 37 public Double toDegreeNumber() { 38 return degree + minute / 60.0 + second / 3600.0; 39 } 40 41 /** 42 * 度 43 */ 44 private Double degree; 45 46 /** 47 * 分 48 */ 49 private Double minute; 50 51 /** 52 * 秒 53 */ 54 private Double second; 55 }
以下是单元测试代码
1 package com.jucheap; 2 3 import com.jucheap.base.CoordinateDTO; 4 import com.jucheap.utils.MapNumberUtil; 5 import org.junit.Test; 6 import org.junit.runner.RunWith; 7 import org.springframework.boot.test.context.SpringBootTest; 8 import org.springframework.test.context.junit4.SpringRunner; 9 10 @RunWith(SpringRunner.class) 11 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 12 public class MapNumberTest { 13 14 /** 15 * 新版图幅规则(GB/T 13989—2012) 16 */ 17 @Test 18 public void mapNumberTest() { 19 Integer scaleNumber = 50000; 20 CoordinateDTO lng = new CoordinateDTO(105.0, 4.0, 25.8); 21 CoordinateDTO lat = new CoordinateDTO(30.0, 56.0, 50.6); 22 23 String mapNumber = MapNumberUtil.toMapNumber(lng, lat, scaleNumber); 24 String oldMapNumber = MapNumberUtil.toOldMapNumber(mapNumber); 25 System.out.println(mapNumber); 26 System.out.println(oldMapNumber); 27 } 28 }
测试结果

通过上面的计算公式基本实现了新旧图幅编号的计算。在实际公式编写中,也遇到了很多问题,具体的实现公式,参考如下:
https://wenku.baidu.com/view/0c0c8bc5f80f76c66137ee06eff9aef8951e485e.html
https://wenku.baidu.com/view/5deb7e1aa8114431b90dd8ed.html
https://www.jianshu.com/p/d11c7f7e6613
参考书籍
02.地理信息系统概论(黄杏元) PDF下载
03.地理信息系统导论(陈述彭) PDF下载
04.第一部分 地图投影及其坐标转换公式 PDF下载
05.第二部分 非地图投影坐标运算公式 PDF下载
06.GIS空间分析原理与方法(刘湘楠) PDF下载
07.地图学与地图绘制 PDF下载
08.地图学基础 PDF下载
09.地图投影 PDF下载
10.地图投影变换原理与方法 PDF下载
11.地理信息系统原理、应用与工程 (第2版) PDF下载
12.地籍测量学 PDF下载
13.地貌学原理(第3版) PDF下载
14.新编地图学实习教程 PDF下载
15.普通地图制图中的数学方法 PDF下载
16.景观生态学——格局、过程、尺度与等级 PDF下载
17.海洋地图学 PDF下载
18.自然地理学 第2版 PDF下载
19.自然地理学野外实习 原理、方法与实践 PDF下载
20.地图艺术设计 PDF下载
下载地址:http://bbs.3s001.com/thread-151638-1-1.html
浙公网安备 33010602011771号