目录

前言

一、坐标格式转换

1、数据初认识

2、将区域分割成多个点

3、数据转换

4、数据转换调用

二、WebGIS展示空间位置信息

1、定义底图

2、Polygon的可视化

3、实际效果

三、总结


前言

        通常而言,海事部门如海事局,通常会在所述的管辖区域内进行航行管控。比如会在海面上进行采砂作业、或者是拖带作业、人员落水、沉船碍航等作业需求。这些消息通常会先由各海域属地的海事局进行消息的发布,可以在他们的官方网站上看到,如以下的信息:

使用Leaflet进行船舶航行警告区域绘制实战_数据

闽航警34/24  台湾海峡

2024年7月15日至2024年10月15日,每日24小时,在以下四点连线范围内的安全作业区进行采砂作业,24-12.27N  118-24.04E ,24-10.25N  118-26.30E ,24-08.17N  118-24.05E ,

24-10.19N  118-21.78E 。无关船舶禁止驶入。

福建海事局。

        在图上的信息中, 其实就包含一些很重要的时空信息,比如预警的时间是2024年7月15日到10月15日,中间差不多有三个月的时间,在这三个月的时间内,是不允许其它的船舶驶入上述的区域内的。作为GIS开发者,我们除了重点关注预警的时间范围,更关心的是空间范围。如果假定是一些商用船舶,我们就可以提取这些空间数据,在进行航线规划的时候,绕开这些作业区域,不仅能保证航行安全,也是加快作业效率。关注新闻的朋友一定知道,一些重要的行动需要对某一些区域进行管控的,也是会经过地方海事局进行公开位置,然后在规定的时间内进行。而这些区域不仅仅是一块区域,而是多个区域同时进行,从而锤炼不同的队伍的实战能力。如下图发布的航行警告中就包括6个区域(即面数据)。

使用Leaflet进行船舶航行警告区域绘制实战_Leaflet航行区域告警_02

        本文即以Leaflet为例,重点讲解如何将上述的空间信息转换为可以在地图上浏览的位置信息。首先使用Java语言将上面的涉及的空间坐标(度分秒)调整为常见的经纬度坐标。然后根据不同的点连接成多个面,最后将这些Polygon面数据叠加到Leaflet中,从而实现航行警告中的相关区域和地图的融合。通过本文,您可以掌握怎么使用Java把度分秒坐标转为标准格式,还可以掌握在Leaflet当中如何把这些Polygon进行空间叠加。

一、坐标格式转换

        其实度分秒这种格式的坐标在日常生活中也是非常常见的,读起来也比较顺畅。但是通常,我们在WebGIS上进行位置展示时,还是习惯于使用经纬度格式的坐标。因此这里主要讲解如何将度分秒的坐标转为标准经纬度信息。转换它的方法其实很简单,本人比较熟悉Java语言,因此后台的转换方法均使用Java编写,除了使用Java来开发这个功能,其他的编程语言同样可以实现。

1、数据初认识

        要想实现格式的转换,首先我们要对原文中的格式进行解释。可以看到在原文中的,它的具体内容中包含位置面信息内容如下:

区域一为下列4点连线水域:     25°15′26"N/120°29′20"E,24°50′30"N/120°05′45"E,     25°04′32"N/119°51′22"E,25°28′12"N/120°14′30"E。   
区域二为下列4点连线水域:     26°07′00"N/121°57′00"E,25°30′00"N/121°57′00"E,     25°30′00"N/121°28′00"E,26°07′00"N/121°28′00"E。    
区域三为下列4点连线水域:     25°34′00"N/122°50′00"E,25°03′00"N/122°50′00"E,     25°03′00"N/122°11′00"E,25°34′00"N/122°11′00"E。   
区域四为下列4点连线水域:     22°56′00"N/122°40′00"E,23°38′00"N/122°51′00"E,     23°38′00"N/123°23′00"E,22°56′00"N/123°09′00"E。    
区域五为下列4点连线水域:     21°14′00"N/121°33′00"E,21°33′00"N/121°18′00"E,     21°07′00"N/120°43′00"E,20°48′00"N/120°59′00"E。     
区域六为下列4点连线水域:     22°43′00"N/119°14′00"E,22°10′00"N/119°06′00"E,     21°33′00"N/120°29′00"E,22°09′00"N/120°32′00"E。
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

        每个区域的都是这种形式的数据,这也为我们进行统一转换提供了有效的数据格式的基础。可以看到,每个区域都是一条数据,不同的点位(point)之间用逗号分隔,而坐标点内部使用/来分割。因此我们要想识别每一个区域的信息,其实也很简单。首先将字符串按逗号分隔,这样就可以形成多个点位的度分秒字符串。然后再分割/字符串,从而得到度分秒的位置。

2、将区域分割成多个点

按照前面介绍得分割方法,首先根据逗号,将目标区域字符串分割成多个点。关键的代码如下:

/**
 * 	将四个点的字符串统一解析为经纬度数组
 * @param bboxString
 */
private static void str2Point(String bboxString) {
	//将经纬度单位中的分改为英文状态下的分
	bboxString = bboxString.replaceAll("′", "\'");
	String [] points = bboxString.split(",");
	for (String str : points) {
		String [] latLon = str2LatLon(str);
		System.out.println(str + " = (" + latLon[0] + "," +latLon[1] + ")");
	}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

        将经纬度单位中的分改为英文状态下的分是为了在后续的解析中进行数据容错。

3、数据转换

        将点从区域点字符串提取出来之后,我们需要将数据进行提取和转换两个步骤。提取是按照/将字符进行分割,而转换是将度分秒的数据转换为标准的经纬度数据。关键代码如下:

/**
* 	将单个度分秒坐标转经纬度坐标数组
* @param latLonStr
* @return
*/
private static String [] str2LatLon(String latLonStr) {
	String lat = latLonStr.split("\\/")[0];
    String lon = latLonStr.split("\\/")[1];
    String latSuffix = lat.substring(lat.length() -1 );//取出纬度后缀N,S	
    String lonSuffix = lon.substring(lon.length() -1 );//取出纬度后缀W,E
    String newLat =String.valueOf(LatLngUtil.dfm2LatLng(lat.substring(0,lat.length()-1)));
    String newLon =String.valueOf(LatLngUtil.dfm2LatLng(lon.substring(0,lon.length()-1)));
    newLat = latSuffix.equalsIgnoreCase("S") ?  "-" + newLat : newLat;
    newLon = lonSuffix.equalsIgnoreCase("W") ?  "-" + newLon : newLon;
    return new String[]{newLat,newLon};
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

        这里还是将经纬度转换的方法提供出来,防止大家在第一次看的时候有所迷糊。

/**
 * 度分秒转经纬度
 *
 * @param dms 116°25'7.85"
 * @return 116.418847
 */
public static double dfm2LatLng(String dms) {
    if (dms == null) return 0;
    try {
        dms = dms.replace(" ", "");
        String[] str2 = dms.split("°");
        if (str2.length < 2) return 0;
        int d = Integer.parseInt(str2[0]);
        String[] str3 = str2[1].split("\'");
        if (str3.length < 2) return 0;
        int f = Integer.parseInt(str3[0]);
        String str4 = str3[1].substring(0, str3[1].length() - 1);
        double m = Double.parseDouble(str4);

        double fen = f + (m / 60);
        double du = (fen / 60) + Math.abs(d);
        if (d < 0) du = -du;
         return Double.parseDouble(String.format("%.7f", du));
      } catch (Exception e) {
          e.printStackTrace();
      }
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.

4、数据转换调用

        调用的函数比较简单,这里采用test的测试用例调用为例进行讲解,当然这里不限于使用何种方法。

@Test
public void testBoundaryConvert() {
	/*
	区域一为下列4点连线水域:   25°15′26"N/120°29′20"E,24°50′30"N/120°05′45"E,25°04′32"N/119°51′22"E,25°28′12"N/120°14′30"E
	区域二为下列4点连线水域:     26°07′00"N/121°57′00"E,25°30′00"N/121°57′00"E,25°30′00"N/121°28′00"E,26°07′00"N/121°28′00"E  
	区域三为下列4点连线水域:     25°34′00"N/122°50′00"E,25°03′00"N/122°50′00"E,25°03′00"N/122°11′00"E,25°34′00"N/122°11′00"E   
	区域四为下列4点连线水域:     22°56′00"N/122°40′00"E,23°38′00"N/122°51′00"E,23°38′00"N/123°23′00"E,22°56′00"N/123°09′00"E    
	区域五为下列4点连线水域:     21°14′00"N/121°33′00"E,21°33′00"N/121°18′00"E,21°07′00"N/120°43′00"E,20°48′00"N/120°59′00"E     
	区域六为下列4点连线水域:     22°43′00"N/119°14′00"E,22°10′00"N/119°06′00"E,21°33′00"N/120°29′00"E,22°09′00"N/120°32′00"E   
	*/
	List<String> bboxList = new ArrayList<String>();
		bboxList.add("25°15′26\"N/120°29′20\"E,24°50′30\"N/120°05′45\"E,25°04′32\"N/119°51′22\"E,25°28′12\"N/120°14′30\"E");//区域一
		bboxList.add("26°07′00\"N/121°57′00\"E,25°30′00\"N/121°57′00\"E,25°30′00\"N/121°28′00\"E,26°07′00\"N/121°28′00\"E");//区域二
		bboxList.add("25°34′00\"N/122°50′00\"E,25°03′00\"N/122°50′00\"E,25°03′00\"N/122°11′00\"E,25°34′00\"N/122°11′00\"E");//区域三
		bboxList.add("22°56′00\"N/122°40′00\"E,23°38′00\"N/122°51′00\"E,23°38′00\"N/123°23′00\"E,22°56′00\"N/123°09′00\"E");//区域四
		bboxList.add("21°14′00\"N/121°33′00\"E,21°33′00\"N/121°18′00\"E,21°07′00\"N/120°43′00\"E,20°48′00\"N/120°59′00\"E");//区域五
		bboxList.add("22°43′00\"N/119°14′00\"E,22°10′00\"N/119°06′00\"E,21°33′00\"N/120°29′00\"E,22°09′00\"N/120°32′00\"E");//区域六
		int index = 1;
		for(String bbox : bboxList) {
			System.out.println("区域"+index+"位置字符串:" + bbox);
			str2Point(bbox);
			System.out.println("--------------------------------------------------------------");
			index ++;
		}
	}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

        在控制台中执行上述代码之后,可以看到以下结果,说明执行成功,同时数据均已成功的转换。

使用Leaflet进行船舶航行警告区域绘制实战_数据_03

        解析的数据内容如下(取其中的一个点为例):

区域1位置字符串:25°15′26"N/120°29′20"E,24°50′30"N/120°05′45"E,25°04′32"N/119°51′22"E,25°28′12"N/120°14′30"E
25°15'26"N/120°29'20"E = (25.2572222,120.4888889)
24°50'30"N/120°05'45"E = (24.8416667,120.0958333)
25°04'32"N/119°51'22"E = (25.0755556,119.8561111)
25°28'12"N/120°14'30"E = (25.47,120.2416667)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

         在经过上述的方法将度分秒坐标转换为标准经纬度坐标后,我们就可以将坐标叠加到Leaflet当中进行空间展示,下一节来深入阐述。

二、WebGIS展示空间位置信息

        当我们得到了经过转换过的空间标准经纬度坐标信息后,我们就可以将这些空间数据转换为面数据或者点数据,然后使用WebGIS框架,比如Leaflet等进行信息的几种展示。

1、定义底图

        定义定图的方法非常简单,这里我们直接给出在应用程序中使用天地图的方法,请注意如果在拷贝这里的代码时,一定要注意替换这里的令牌。

var map = L.map('mapid').setView([23.785345, 120.948486], 7);
	
var tdt_client_key = "473af7dc18cafb6b993616a0ce8e1ead";//天地图客户端的key

L.tileLayer('http://t0.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=' +tdt_client_key, {
		maxZoom: 18,
		attribution: 'Map data © <a href="http://www.tianditu.gov.cn/">TianDiTu</a>, <a href="http://www.esri.com/">Esri</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>'
}).addTo(map);
	
//标签 
L.tileLayer('http://t1.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk='+ tdt_client_key, {maxZoom: 18
}).addTo(map);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

2、Polygon的可视化

        在定义的基础的底图之后,下面将第一节转换好的所有的坐标进行Polygon空间展示。关键代码如下:

var style = {"color":"red","fillColor":"red","weight":2};
	
	L.polygon([
		[25.2572222,120.4888889],
		[24.8416667,120.0958333],
		[25.0755556,119.8561111],
		[25.47,120.2416667]
	],style).addTo(map).bindPopup("区域一.");
	
	L.polygon([
		[26.1166667,121.95],
		[25.5,121.95],
		[25.5,121.4666667],
		[26.1166667,121.4666667]
	],style).addTo(map).bindPopup("区域二.");
	
	L.polygon([
		[25.5666667,122.8333333],
		[25.05,122.8333333],
		[25.05,122.1833333],
		[25.5666667,122.1833333]
	],style).addTo(map).bindPopup("区域三.");
	
	L.polygon([
		[22.9333333,122.6666667],
		[23.6333333,122.85],
		[23.6333333,123.3833333],
		[22.9333333,123.15]
	],style).addTo(map).bindPopup("区域四.");
	
	L.polygon([
		[21.2333333,121.55],
		[21.55,121.3],
		[21.1166667,120.7166667],
		[20.8,120.9833333]
	],style).addTo(map).bindPopup("区域五.");
	
	L.polygon([
		[22.7166667,119.2333333],
		[22.1666667,119.1],
		[21.55,120.4833333],
		[22.15,120.5333333]
	],style).addTo(map).bindPopup("区域六.");
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.

        请注意,在上述的例子中,我们使用一个统一的样式选型,style,它表示展示的Polygon面的颜色和填充颜色都是红色,如果您想设置自己喜欢的颜色也是可以的。

3、实际效果

        经过上述的两个步骤,我们已经实现了地图对象的定义和绑定,同时我们将转换好的坐标信息进行赋值,让6个面在地图上进行可视化。

使用Leaflet进行船舶航行警告区域绘制实战_连线_04

        以上的红色区域就是我们转换的六个区域,不知道大家看了这六个区域,有没有勾起点啥记忆呢。 这里不多说,大家可以去相关网站搜索这个区域之前发生过什么就大致清楚了。

三、总结

        以上就是本文的主要内容,本文即以Leaflet为例,重点讲解如何将上述的空间信息转换为可以在地图上浏览的位置信息。首先使用Java语言将上面的涉及的空间坐标(度分秒)调整为常见的经纬度坐标。然后根据不同的点连接成多个面,最后将这些Polygon面数据叠加到Leaflet中,从而实现航行警告中的相关区域和地图的融合。通过本文,您可以掌握怎么使用Java把度分秒坐标转为标准格式,还可以掌握在Leaflet当中如何把这些Polygon进行空间叠加。行文仓促,定有不足之处,真诚期待各位专家朋友在评论区批评指正,不甚感激。