新图幅号计算和图幅框计算

本文的图幅号计算直接引用了博客园地址:使用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类型的计算 导致。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值