本文的图幅号计算直接引用了博客园地址:使用Java实现的新版地图图幅编号计算公式(新版图幅规则(GB/T 13989—2012)) - JuCheap - 博客园
的代码,微小的调整了一下,计算图幅框四至是参照 地图学第02章:地图的分幅和编号来实现,地图学第02章 地图的分幅和编号 (新) - 道客巴巴
/**
* <p>
* 地图坐标对象
* </p>
*
* @Author jucheap
* @Email jucheap@jucheap.com
*/
public class CoordinateDTO {
/**
* ctor
* @param degree 度
* @param minute 分
* @param second 秒
*/
public CoordinateDTO(Double degree, Double minute, Double second) {
this.degree = degree;
this.minute = minute;
this.second = second;
}
/**
* 转换成十进制的度数
*
* @return 十进制的度数
*/
public Double toDegreeNumber() {
return degree + minute / 60.0 + second / 3600.0;
}
/**
* 度
*/
private Double degree;
/**
* 分
*/
private Double minute;
/**
* 秒
*/
private Double second;
public Double getDegree() {
return degree;
}
public void setDegree(Double degree) {
this.degree = degree;
}
public Double getMinute() {
return minute;
}
public void setMinute(Double minute) {
this.minute = minute;
}
public Double getSecond() {
return second;
}
public void setSecond(Double second) {
this.second = second;
}
}
/**
* <p>
* 地图新图幅编号和图幅框计算
* </p>
*/
public class MapNumberUtil {
static GeometryFactory gf = new GeometryFactory();
/**
* 从新图幅号计算角点坐标(50w-5千) 参照:地图学第02章 地图的分幅和编号
* @param newNumber
* @return
*/
public static List<Point> CalWNPoint(String newNumber)
{
int a = letterToNumber(newNumber.substring(0,1));
int b = Integer.parseInt(newNumber.substring(1, 3));
int c = Integer.parseInt(newNumber.substring(4, 7));
int d = Integer.parseInt(newNumber.substring(7));
String scaleLetter = newNumber.substring(3, 4);
int scaleNo = toScaleNum(scaleLetter);
double delX = getLongitudeDiff(scaleNo);
double delY = getLatitudeDiff(scaleNo);
List<Point> resultLst = new ArrayList<Point>();
double x = (b-31)*6+(d-1)*delX;
double y = (a-1)*4+((4/delY)-c)*delY;
Point mPnt1 = gf.createPoint(new Coordinate(x, y));
resultLst.add(mPnt1);
Point mPnt2 = gf.createPoint(new Coordinate(x+delX, y));
resultLst.add(mPnt2);
Point mPnt3 = gf.createPoint(new Coordinate(x+delX, y+delY));
resultLst.add(mPnt3);
Point mPnt4 = gf.createPoint(new Coordinate(x, y+delY));
resultLst.add(mPnt4);
int count = 1;
for(Point p : resultLst)
{
System.out.println("第"+count+"个点坐标:"+p.toText()+"("+trandu2m(p.getX())+","+trandu2m(p.getY())+")");
count++;
}
return resultLst;
}
/**
* 新版图幅规则(GB/T 13989—2012)
* @param longitude 经度
* @param latitude 纬度
* @param scaleNumber 缩放比例(1000000/500000/100000/50000/10000/5000)
* @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()));
String result = sb.toString();
if(scaleNumber == 1000000)
{
result = result.substring(0,3);
}
return result;
}
/**
* 生成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 newMapNumber 新图幅编号
* @return 旧图幅编号
*/
public static String toOldMapNumber(String newMapNumber) {
if (newMapNumber.length() != 3 && newMapNumber.length() != 10) {
return "";
}
char[] newMapNumberChars = newMapNumber.toCharArray();
String bigRowNo = String.valueOf(newMapNumberChars[0] - 'A' + 1);
String bigColNo = newMapNumber.substring(1, 3);
if (newMapNumber.length() == 3) { //100W
return String.format("%s-%s", bigRowNo, bigColNo);
} else {
// 新图幅号的行列代码,col_number为行代码,col_number为列代码
Integer row_number = Integer.valueOf(newMapNumber.substring(4, 7));
Integer col_number = Integer.valueOf(newMapNumber.substring(7, 10));
// 提取行代码和列代码,若非数字,则表示图幅号格式不正确
char mapScaleNumber = newMapNumberChars[3];
if (mapScaleNumber == 'B') { //50W
//region 50W
if (row_number > 2 || col_number > 2){
return "";
}
//旧图幅号对应的地图代码
int X = (row_number - 1) * 2 + (col_number - 1) + 1;
return String.format("%s-%s-%s", bigRowNo, bigColNo, toUpperCode(X));
} else if (mapScaleNumber == 'C') {//25W
if (row_number > 4 || col_number > 4) {
return "";
}
//旧图幅号对应的地图代码
int X = (row_number - 1) * 4 + (col_number - 1) + 1;
String code = String.valueOf(X);
return String.format("%s-%s-[%s]", bigRowNo, bigColNo, code);
} else if (mapScaleNumber == 'D') {//10W
if (row_number > 12 || col_number > 12){
return "";
}
//旧图幅号对应的地图代码
int X = (row_number - 1) * 12 + (col_number - 1) + 1;
//String code = String.format("%03d", X);
String code = String.valueOf(X);
return String.format("%s-%s-%s", bigRowNo, bigColNo, code);
} else if (mapScaleNumber == 'E') { //5W
//region 5W
if (row_number > 24 || col_number > 24) {
return "";
}
//10W地形图对应的行号
int H10 = (row_number - 1) / 2 + 1;
//10W地形图对应的列号
int L10 = (col_number - 1) / 2 + 1;
//10W旧图幅号对应的地图代码
int X10 = (H10 - 1) * 12 + (L10 - 1) + 1;
//String code = String.format("%03d", X10);
String code = String.valueOf(X10);
//旧图幅号对应的地图代码
int X = (row_number - 2 * H10 + 1) * 2 + (col_number - 2 * L10 + 1) + 1;
return String.format("%s-%s-%s-%s", bigRowNo, bigColNo, code, toUpperCode(X));
//endregion
} else if (mapScaleNumber == 'F') { //5W地形图对应的行号
int H5 = (row_number - 1) / 2 + 1;
//5W地形图对应的列号
int L5 = (col_number - 1) / 2 + 1;
//10W地形图对应的行号
int H10 = (H5 - 1) / 2 + 1;
//10W地形图对应的列号
int L10 = (L5 - 1) / 2 + 1;
Integer X2 = (row_number - 2 * H5 + 1) * 2 + (col_number - 2 * L5 + 1) + 1;
Integer X5 = (H5 - 2 * H10 + 1) * 2 + (L5 -2 * L10 + 1) + 1;
Integer X10 = (H10 - 1) * 12 + (L10 - 1) + 1;
return String.format("%s-%s-%s-%s-%s", bigRowNo, bigColNo, X10, toUpperCode(X5), X2);
}
//图幅号格式不正确
return "";
}
}
/**
* 度转换为度分秒
* @param d
* @return
*/
public static String trandu2m(double d) {
try {
String str = "" + d;
int p = str.indexOf(".");
int dt = Integer.parseInt(str.substring(0, p));
d = d - dt;
double M = d * 60;
int mt = (int) M;
M = (M - mt) * 60;
if (Math.abs(M - 60) < 0.001) {
M = 0;
mt = mt + 1;
}
if (mt == 60) {
dt = dt + 1;
mt = 0;
}
return ""+dt+"°"+mt+"′"+M+"″";
} catch(Exception e) {
return e.getMessage();
}
}
/**
* 字母转数字 A-Z :1-26
* @param letter
* @return
*/
public static int letterToNumber(String letter) {
int length = letter.length();
int num = 0;
int number = 0;
for(int i = 0; i < length; i++) {
char ch = letter.charAt(length - i - 1);
num = (int)(ch - 'A' + 1) ;
num *= Math.pow(26, i);
number += num;
}
return number;
}
/**
* 数字转字母 1-26 : A-Z
* @param num
* @return
*/
private static String numberToLetter(int num) {
if (num <= 0) {
return null;
}
String letter = "";
num--;
do {
if (letter.length() > 0) {
num--;
}
letter = ((char) (num % 26 + (int) 'A')) + letter;
num = (int) ((num - num % 26) / 26);
} while (num > 0);
return letter;
}
/**
* 获取缩放比例代码
* @param scaleNumber 缩放比例(如50000,代表1:50000)
* @return 缩放比例代码
*/
private static String toScaleCode(int scaleNumber) {
switch (scaleNumber) {
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
* @return 缩放比例代码
*/
private static int toScaleNum(String scaleNumber) {
switch (scaleNumber) {
case "B":
return 500000;
case "C":
return 250000;
case "D":
return 100000;
case "E":
return 50000;
case "F":
return 25000;
case "G":
return 10000;
case "H":
return 5000;
case "I":
return 2000;
case "J":
return 1000;
case "K":
return 500;
default:
return 0;
}
}
/**
* 获取经差值
*
* @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 0.25;
case 25000:
return 0.125;
case 10000:
return 0.0625;
case 5000:
return 0.03125;
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 1.0 / 3;
case 50000:
return 1.0 / 6;
case 25000:
return 1.0/12.0;
case 10000:
return 1.0/24.0;
case 5000:
return 1.0/48.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;
}
}
}
几何有部分代码使用了geotools:pom.xml
<!-- geotools -->
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>19.4</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-geojson</artifactId>
<version>19.4</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-main</artifactId>
<version>19.4</version>
</dependency
测试:
public static void main(String[] args) {
//根据经纬度,比例尺获取所在图幅号
Integer scaleNumber = 5000;
CoordinateDTO lng = new CoordinateDTO(115.0, 14.0, 24.0);
CoordinateDTO lat = new CoordinateDTO(28.0, 36.0, 17.0);
String mapNumber = MapNumberUtil.toMapNumber(lng, lat, scaleNumber);
System.out.println(mapNumber);
//从新图幅号计算所在图框四至
MapNumberUtil.CalWNPoint(mapNumber);
}
--------------------------------------
H50H163040
第1个点坐标:POINT (115.21875 28.604166666666668)(115°13′7.5″,28°36′15.000000000004263″)
第2个点坐标:POINT (115.25 28.604166666666668)(115°15′0.0″,28°36′15.000000000004263″)
第3个点坐标:POINT (115.25 28.625)(115°15′0.0″,28°37′30.0″)
第4个点坐标:POINT (115.21875 28.625)(115°13′7.5″,28°37′30.0″)
感觉在小数位的计算上应该是存在一些精度误差的,可能来自于Double类型的计算 导致。