目录
前言
一、细直箭头和突击方向的类设计
1、总体类图
2、对象区别
二、标绘绘制的具体实现
1、绘制时序图
2、相关点的具体绘制
3、最终的成果
三、总结
前言
今天是10月1日国庆节,迎来我们伟大祖国75周年的华诞。有国才有家,在这里首先祝我们伟大的祖国繁荣昌盛。放眼这个世界,俄乌战争时至今日,尚未结束,中东战场现在火药味浓烈,简直就是一个巨大的火药桶。国际局势很紧张,感恩在华夏,可以安心放假休假。祝各位小伙伴们假期愉快。
在之前的博客中分享了一次关于如何在Leaflet中进行直箭头的对象标绘。在实际的标绘过程中,我们需要展示一定宽度的箭头,基于Leaflet和天地图的直箭头标绘实战-源码分析。如下面的图片展示的效果:
在上图中,有两种类型的标绘对象,第一个是细直箭头;第二个是突击方向。这两类对象其实是差不多的,在后面的讲解过程中,你就会发现。突击方向是细直箭头的子类,具体内容在后续的章节中进行介绍。
本文将重点讲解在Leaflet中如何进行细直箭头和突击方向的绘制,首先使用面向对象的方法对细直箭头和突击方向的属性和方法进行介绍,然后重点介绍在绘制细直箭头和突击方向对象的各个绘制点的计算过程,最后给出一个实例过程。如果您对这两种标绘对象的绘制有兴趣,不妨来看看。
一、细直箭头和突击方向的类设计
在前面的内容中,我们介绍了细直箭头和突击方向这两种标绘对象。突击方向是细直箭头的子类,为了更详细展示两者的区别和联系。我们使用面向对象的方式来简单介绍一下。
1、总体类图
根据源代码,我们把这两个对象的总体类图绘制如下,供大家参考:
通过以上的图中,我们可以看到,L.PlotUtils是公共的绘制对象,提供了很多的计算方法,比如:把地理坐标和平面坐标的互相转换,计算贝塞尔曲线,计算相关点等方法。这些方法在进行相应的对象绘制时非常重要。
2、对象区别
L.Plot是所有标绘对象的基类,包含了各个点的信息,包括控制点信息。基于L.Plot对象,扩展两个箭头对象,细直箭头和突击方向。
/*** 突击方向*/
L.Plot.AssaultDirection = L.Plot.FineArrow.extend({options: {tailWidthFactor: 0.2,neckWidthFactor: 0.25,headWidthFactor: 0.3,headAngle: Math.PI / 4,neckAngle: Math.PI * 0.17741,fixPointCount: 2},initialize: function (latlngs, options) {L.setOptions(this, options);this.type = L.PlotTypes.ASSAULT_DIRECTION;this.setPoints(latlngs)}
});L.Plot.assaultDirection = function (latlngs, options) {return new L.Plot.AssaultDirection(latlngs, options);
};
以上简单介绍细直箭头和突击方向的类继承关系。了解继承关系对于理解对象和构造有很大的作用。通过上面的类定义可以看到,细直箭头与突击方向的最大区别就是箭头对象的宽度和角度有一定的区别。当然,大家可以通过参数传入的方式来进行属性的控制。两者的区别就是:
options: {tailWidthFactor: 0.15,//尾部宽度倍数neckWidthFactor: 0.2,//颈部宽度倍数headWidthFactor: 0.25,//头部宽度倍数headAngle: Math.PI / 8.5,//头部角度neckAngle: Math.PI / 13,//颈部角度fixPointCount: 2}
二、标绘绘制的具体实现
我们仔细来看细直箭头,可以发现。在绘制过程中,我们只需要确定两个点,即开始位置和结束位置,下图中的pnt1和pnt2。而构成整体面的点为不包括pnt1以外的所有点。绘制原理如下:
直箭头是有两个点组成的,左下角为起点(pnt1),右上角为终点(pnt2)。那么,这个直箭头是如何来的?其实,这个直箭头就是通过起止两个点计算得到的一个由七个点连成的多边形Polygon。
通过两个点创建标绘对象的方法可以使用以下公式来表达:
其中:polygon(pnt1,pnt2)表示求解根据起始位置构建箭头对象;
tailLeft表示尾部左边点坐标,neckLeft表示颈部左边点位置,headLeft表示头部左边点位置,pnt2表示结束点位置,headRight 表示头部右边点位置,neckRight表示颈部右边点位置,tailRight表示尾部右边点位置。从尾部左边点位置开始,按照顺时针的顺序连接这7个点即完成一个面的构建。然后把平面左边转为地理左边面,最后叠加在地图上展示。突击方向除了箭头的方向扁平程度与细直箭头有所区别,绘制方式是一样的。后续内容中以细直箭头绘制为例。
1、绘制时序图
为了让大家对标绘对象的绘制时序有一个基本的了解,因此这里结合一些关键步骤来讲解相关类的调用过程。
在上图中清晰的展示了在界面中各个对象如何进行调用。涉及的对象有PlotUtils和具体的箭头对象。 那么具体细直箭头是如何根据两个点来生成另外的七个点的过程,我们将在后续的内容中进行详细介绍。
2、相关点的具体绘制
了解了绘制时序及相关的对象之后,我们来具体看一下如何使用两个点生成面的七个关键点。其它的辅助代码再此不再赘述,重点叙述7个点的计算过程。计算方法源码如下所示:
//生成图形
generate: function () {if (this.getPointCount() < 2) {this._setLatLngs([])return;}var pnts = this._proPoints;var pnt1 = pnts[0];var pnt2 = pnts[1];var len = L.PlotUtils.getBaseLength(pnts);//pnt1和pnt2的距离的0.99次幂var tailWidth = len * this.options.tailWidthFactor;//尾部的宽度var neckWidth = len * this.options.neckWidthFactor;//颈部宽度var headWidth = len * this.options.headWidthFactor;//头部宽度var tailLeft = L.PlotUtils.getThirdPoint(pnt2, pnt1, L.PlotConstants.HALF_PI, tailWidth, true);var tailRight = L.PlotUtils.getThirdPoint(pnt2, pnt1, L.PlotConstants.HALF_PI, tailWidth, false);var headLeft = L.PlotUtils.getThirdPoint(pnt1, pnt2, this.options.headAngle, headWidth, false);var headRight = L.PlotUtils.getThirdPoint(pnt1, pnt2, this.options.headAngle, headWidth, true);var neckLeft = L.PlotUtils.getThirdPoint(pnt1, pnt2, this.options.neckAngle, neckWidth, false);var neckRight = L.PlotUtils.getThirdPoint(pnt1, pnt2, this.options.neckAngle, neckWidth, true);var pList = [tailLeft, neckLeft, headLeft, pnt2, headRight, neckRight, tailRight];this._setLatLngs([L.PlotUtils.unProPoints(pList)]);this.redraw();
}
第一步:计算尾部左右两个坐标点位置。想要计算尾部两个坐标的位置,首先我们需要根据pnt1、和pnt2来计算两个点的长度,这样整体箭头的长度大致就确定了。然后根据细直箭头的尾部宽度的配置,在源码中可以看到设置的比例是0.15,具体是前面计算长度的0.15。最后计算获取第三点(起点到终点的连线上,以终点为轴,旋转angle后,距离终点distance的点)。
var len = L.PlotUtils.getBaseLength(pnts);//pnt1和pnt2的距离的0.99次幂
根据长度和设置的尾部宽度的系数可以得到尾部的宽度一个值,然后代入到计算第三个点的计算公式中,即可:
var tailLeft = L.PlotUtils.getThirdPoint(pnt2, pnt1, L.PlotConstants.HALF_PI, tailWidth, true);
计算第三个点的坐标位置具体方法如下,这个方法在后续的过程中经常用到。
//获取第三点(起点到终点的连线上,以终点为轴,旋转angle后,距离终点distance的点)
L.PlotUtils.getThirdPoint = function (startPnt, endPnt, angle, distance, clockWise) {var azimuth = L.PlotUtils.getAzimuth(startPnt, endPnt);//获取终点相对于起点的方位角var alpha = clockWise ? azimuth + angle : azimuth - angle;var dx = distance * Math.cos(alpha);var dy = distance * Math.sin(alpha);return [endPnt[0] + dx, endPnt[1] + dy];
};
下面结合图形来讲解tailLeft和tailRight的具体计算过程。对trailLeft这个点来说,就是pnt1沿着pnt2的方向逆时针旋转90度()的方向上,距离pnt1有trailWidth远的点。反方向上距离pnt2有trailWidth远的点就是trailRight。
通过以上步骤即可获得尾部的两个点的坐标信息,然后我们依次来计算neck和head的两个点的位置信息。
第二步:计算headLeft和headRight两个点。而head的两个坐标点的计算则反过来,以pnt2为起点按照逆时针和顺时针来进行坐标点的计算。偏转的角度为:Math.PI / 8.5,//头部角度。
第三步:获取neckLeft和neckRight两个点的绘制与前两个点的绘制过程类似,这里不再进行赘述。主要步骤分两个,第一个是计算neckWidth,然后根据设置的neck的偏转角,这里取:
neckAngle: Math.PI / 13,//颈部角度,大家可以根据实际情况来进行相应情况的设置。
最后,根据计算获得的这7个点,连成一个Polygon面,然后叠加到地图上即可实现一个细直箭头。同理突击方向的箭头绘制也是一样的处理方法。
3、最终的成果
为了展示细直箭头和突击方向的最终成果,我们结合天地图,使用Leaflet来进行一个两个结合的综合展示功能开发。动态绘制的代码如下:
// 直箭头
function addStraightArrow() {L.Plot.straightArrow([[28.17629, 112.923746],[28.188471, 112.948208]]).addTo(this.plotLayer);
}// 细直箭头
function addFineArrow() {L.Plot.fineArrow([[28.167286, 112.969236],[28.180224, 112.976618]]).addTo(this.plotLayer);L.Plot.fineArrow([[28.19248, 112.983398],[28.173415, 113.006058]]).addTo(this.plotLayer);
}// 突击方向
function addAssaultDirection() {L.Plot.assaultDirection([[28.180981, 112.901001],[28.200045, 112.937222]]).addTo(this.plotLayer);L.Plot.assaultDirection([[28.143903, 112.964344],[28.179165, 112.94117]]).addTo(this.plotLayer);L.Plot.assaultDirection([[28.211995, 112.984772],[28.202163, 112.962284]]).addTo(this.plotLayer);
}
最终执行的效果如下图所示:
三、总结
以上就是本文的主要内容,本文将重点讲解在Leaflet中如何进行细直箭头和突击方向的绘制,首先使用面向对象的方法对细直箭头和突击方向的属性和方法进行介绍,然后重点介绍在绘制细直箭头和突击方向对象的各个绘制点的计算过程,最后给出一个实例过程。行文仓促,难免有许多不足之处,如有不足,还请各位专家和博主在评论区中留下真知烁见,不才定当感激不尽。
博文编写过程中,参考了下列内容(但本文做了更详细的介绍)在此表示感谢:
1、基于Leaflet实现标绘——直箭头。