目录
前言
一、Leaflet的特种标绘库
1、特种标绘对象的定义
2、Plot基类定义
3、直线箭头的设计与实现
二、在天地图中进行对象绘制
1、引入天地图资源
2、标绘对象的调用时序
3、实际调用过程
三、总结
前言
在博客中介绍过geoman标绘的具体实现,使用Leaflet GeoMan结合天地图进行自由标绘实战,基于GeoMan的标绘只能包含了常见的点、线、面、矩形的空间对象的标绘。在一些面向特种行业的应用中,我们会遇到特种标绘。比如用来表示敌我双方态势的战斗标绘,比如在一场战斗中的敌我接敌,各方的支援力量的介入,战斗的结果等等过程需要进行集中的标绘。如下图所示:
以上对象的绘制比之前的基于Geoman的点、线、面等信息的生成稍微要复杂一些。当然您使用一些开源库进行系统的构建,这样能加快开发的速度。但是如果我们想实现自由业务的封装,就需要进行相应的改造才可以,比如我们需要定义箭头的偏转角,控制斜率,生成集结地等曲面时更加的平滑等等,这些功能的实现都是需要进行自己的定制和改造的。因此我们有必要在Leaflet中实现上述的功能,同时掌握如何来进行自定义的绘制。
一般的态势标绘的类型可以包含以下几种:直箭头、细直箭头、突击方向、进攻方向、进攻方向(尾)、分队战斗行动、分队战斗行动(尾)、钳击、聚集地等。不同的图形绘制过程不一样,根据绘制算法的不同,计算过程也不尽相同。后续会依据不同的标绘对来进行绘制过程解析。本文主要以直线箭头的绘制重点讲解在Leaflet中对上述对象的封装,相关类的功能介绍等,首先介绍一个基于Leaflet的标绘基础库,其次介绍这个库的基本结构,相关类属性和方法的定义,然后基于时序图来介绍相关API的调用,最后生成一个直线箭头的实例。如果对标绘感兴趣的朋友,可以不妨看过来。
一、Leaflet的特种标绘库
目前基于Leaflet的特种标绘库开源的不多,但是有一个基础的开源库可以提供标绘对象的生成服务,leaflet_plot,大家可以下载相关的源代码进行学习。它的开源代码主要包含两个文件,一个是Plots.js和PlotUtil.js,其中PlotUtil.js主要用于相关坐标点的绘制,Plots.js主要用于绘制和展示不同的对象。
1、特种标绘对象的定义
首先,我们来看一下Plots.js,在这个类中,定义了所有的标绘对象,由于对象种类较多,这里仅以直线箭头为例,重点讲解标绘类的设计与实现,其它的对象绘制过程比较复杂,后续再慢慢进行讲解。Plots.js主要包含类型定义、对象工厂的创建、具体对象实现的创建。下面将详细介绍。
在这里,通过javascript代码定义出所有的对象类型,具体有下列几种:
序号 | 类型 | 说明 |
1 | STRAIGHT_ARROW | "straightarrow",//直箭头 |
2 | ASSAULT_DIRECTION | "assaultdirection",//突击方向 |
3 | ATTACK_ARROW | "attackarrow",//进攻方向 |
4 | TAILED_ATTACK_ARROW | "tailedattackarrow",//进攻方向(尾) |
5 | SQUAD_COMBAT | "squadcombat",//分队战斗行动 |
6 | TAILED_SQUAD_COMBAT | "tailedsquadcombat",//分队战斗行动(尾) |
7 | FINE_ARROW | "finearrow",//细直箭头 |
8 | DOUBLE_ARROW | "doublearrow", //钳击 |
9 | GATHERING_PLACE | "gatheringplace",//聚集地 |
2、Plot基类定义
按照后端面向对象的方式将对象的公共父类抽象出来,形成公共的基类。用于基础属性的设置与渲染。其它对象可以在此基础之上进行扩展即可。
L.Plot = {isPlot: function () {return true;},getBaseType: function () {let geojson = this.toGeoJSON()let type = geojson.geometry.typeif (type == 'MultiLineString' || type == 'LineString') {type = 'Polyline'}return type},//设置绘制图形需要的点setPoints: function (latlngs) {this._bounds = new L.LatLngBounds();this._setLatLngs([])this._points = this._convertLatLngs(latlngs) || [];this._proPoints = L.PlotUtils.proPoints(this._points);if (this.getPointCount() >= 1)this.generate();},//设置投影点并更新对应的坐标点setProPoints: function (proPts) {var latlngs = L.PlotUtils.unProPoints(proPts);this.setPoints(latlngs);},//获取控制点坐标getCtrlPoints: function() {let ctrlPts = []switch (this.type) {case L.PlotTypes.SECTOR:case L.PlotTypes.ARC:ctrlPts = this.getPoints();if (ctrlPts.length < 3) {ctrlPts = this._ctrlPnts;}break;case L.PlotTypes.CIRCLE:ctrlPts = this.getPoints();if (ctrlPts.length < 2) {ctrlPts = this._ctrlPnts;}break;default:ctrlPts = this.getPoints();}return ctrlPts;},//获取输入点getPoints: function () {return this._points;},//获取输入点对应的投影点getProPoints: function () {return this._proPoints;},//获取输入的点个数getPointCount: function () {return this._proPoints.length || 0;},toPlotJSON: function () {let setting = {type: this.type,points: this.getPoints(),options: this.options,}return setting},//结束绘制finishDrawing: function () {}
}
Plos类的方法列表如下:
序号 | 方法 | 说明 |
1 | isPlot | 是否特殊标绘,默认true |
2 | getBaseType | 获取基础类型,如:Polyline |
3 | setPoints | 设置绘制图形需要的点 |
4 | setProPoints | 设置投影点并更新对应的坐标点 |
5 | getCtrlPoints | 获取控制点坐标 |
6 | getPoints | 获取输入点 |
7 | getProPoints | 获取输入点对应的投影点 |
8 | getPointCount | 获取输入的点个数 |
9 | toPlotJSON | 转成标绘json |
10 | finishDrawing | 标绘结束事件 |
3、直线箭头的设计与实现
在特种标绘的工厂方法中,有生成直线箭头的方法,为简单起见,将其它的方法进行隐藏说明,关键代码如下所示:。
L.PlotFactory = {};
L.PlotFactory.createPlot = function (type, points, options) {switch (type) {case L.PlotTypes.STRAIGHT_ARROW:return new L.Plot.StraightArrow(points, options);case L.PlotTypes.ASSAULT_DIRECTION:return new L.Plot.AssaultDirection(points, options);}return null;
}
其次我们来看下具体的直线箭头的具体实现:
/*** 直箭头*/
L.Plot.StraightArrow = L.Polyline.extend({includes: L.Plot,options: {fixPointCount: 2,maxArrowLength: 3000000,arrowLengthScale: 8},initialize: function (latlngs, options) {L.setOptions(this, options);this.type = L.PlotTypes.STRAIGHT_ARROW;this.setPoints(latlngs)},//生成图形generate: function () {if (this.getPointCount() < 2) {return;}var pnts = this._proPoints;var pnt1 = pnts[0];var pnt2 = pnts[1];var distance = L.PlotUtils.distance(pnt1, pnt2);var len = distance / this.options.arrowLengthScale;len = len > this.options.maxArrowLength ? this.options.maxArrowLength : len;var leftPnt = L.PlotUtils.getThirdPoint(pnt1, pnt2, Math.PI / 8, len, false);var rightPnt = L.PlotUtils.getThirdPoint(pnt1, pnt2, Math.PI / 8, len, true);let proPts = [pnt1, pnt2, leftPnt, pnt2, rightPnt]this._setLatLngs(L.PlotUtils.unProPoints(proPts));this.redraw();}
});
L.Plot.straightArrow = function (latlngs, options) {return new L.Plot.StraightArrow(latlngs, options);
};
通过代码大家可以看到,在StraightArrow这个类中,其是通过继承Polyline来实现对象的扩展的,在extends扩展实现中,扩展了两个属性和两个方法。
序号 | 参数 | 说明 |
1 | includes: L.Plot, | 表示基类包含Plot对象 |
2 | options | 当前对象扩展属性,包含对象独有的箭头倾角等 |
3 | initialize | 初始化方法 |
4 | generate | 最主要的图形绘制,包括各种角度的计算 |
以上就是特种标绘对象的方法和属性的具体介绍。下面将结合天地来重点讲解如何进行标绘对象的静态标绘,即根据指定坐标来动态绘制。
二、在天地图中进行对象绘制
这里将重点讲解如何在天地图中进行特种对象的标绘。通过具体代码的实现和调用过程的重现,让大家对如何进行标绘有更详细的讲解。
1、引入天地图资源
在地图的展示界面中,引入天地图的在线图源非常重要,在加载影像注记的时候,需要请大家注意替换相应的参数。关键代码如下:
<!DOCTYPE >
<html lang="zh"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1" /><title>Leaflet plot绘制</title><link rel="stylesheet" href="./lib/leaflet.css"/><style>.hello {position: relative;}#mapContainer {width: 100%;height: 600px;background-color: #eee;z-index: 0;}#toolbar {position: absolute;left: 10px;bottom: 10px;list-style: none;margin: 0;padding: 10px;box-shadow: 2px 2px 5px;background-color: rgb(34 34 34 / 50%);border-radius: 0 5px 5px 0;}#toolbar li {line-height: 28px;}</style></head><body><div class="hello"><h1>Leaflet的plot标绘</h1><div id="mapContainer"></div><ul id="toolbar"><li><button onclick="addStraightArrow();">直箭头</button></li><hr><li><button onclick="clearPlots();">清空</button></li></ul></div><script src="./lib/leaflet.js"></script><!-- // 计算工具 --><script src="./lib/PlotUtil.js"></script><!-- // 绘制工具 --><script src="./lib/Plots.js"></script><script>var tdt_client_key = "xxx";//天地图客户端的key//影像底图const tiles = 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:'© <a href="https://www.tianditu.gov.cn/">在线图源使用国家天地图</a> contributors',});var plotLayer;var map = L.map('mapContainer').setView([28.170086, 112.957993], 13).addLayer(tiles);//影像注记const label_tiles = L.tileLayer('http://t0.tianditu.gov.cn/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=' + tdt_client_key, {maxZoom: 18});map.addLayer(label_tiles);plotLayer = L.featureGroup().addTo(map);function clearPlots() {this.plotLayer && this.plotLayer.clearLayers()}// 直箭头function addStraightArrow() {L.Plot.straightArrow([[28.17629, 112.923746],[28.188471, 112.948208]]).addTo(this.plotLayer);}</script></body>
</html>
这里需要注意的是,在引入影像注记的时候,需要根据天地图的规范进行相应图层的替换,这里需要注意的点就是:
http://t0.tianditu.gov.cn/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=
需要注意上述字符串中的cia_w和后面的cia,cia表示图层的名字,这里一定注意,否则地图不能正确加载。
2、标绘对象的调用时序
这里我们使用时序图来进行调用时序的讲解,让大家对标绘对象的绘制和加载过程有更深入的讲解,以下是简单的时序调用视图。
3、实际调用过程
这里以静态标绘为例,未来需要根据绘制事件来进行绘制坐标的获取来进行对象的生成。
绘制对象的关键点生成,这个距离在后面的倾角的计算有很大的作用。
第三点(即倾角坐标点的计算)
最终得到的空间对象如下所示:
最后在在上面的结果中对标注关键参数进行说明。
通过以上四点的计算即完成直线箭头的绘制,将绘制的对象添加到地图即可。
三、总结
以上就是本文的主要内容,本文主要以直线箭头的绘制重点讲解在Leaflet中对上述对象的封装,相关类的功能介绍等,首先介绍一个基于Leaflet的标绘基础库,其次介绍这个库的基本结构,相关类属性和方法的定义,然后基于时序图来介绍相关API的调用,最后生成一个直线箭头的实例。行文仓促,定有不足之处,还请各位专家博友不吝指教,不胜感激。
参考资料:
1、基于Leaflet实现标绘——直箭头。