王强+雷中英
摘 要: 根据开发人员对多边形进行空间操作的需求,研究在开源Leaflet的Javascript库的基础上实现WEBGIS前端的多边形切割功能。给WEBGIS管理平台提供实时在线动态切割和动态显示,避免后台切割以及软件预处理的不必要麻烦。
关键词: Leaflet; WEBGIS; 多边形; 切割
中图分类号:TP311 文献标志码:A 文章编号:1006-8228(2017)02-45-04
0 引言
在一般场景中,对多边形切割的实现基本由软件操作完成或者由后台插件提供切割接口完成,并不是在前端部分实现。因为前端计算量巨大会产生不尽人意的效率问题,也会出现浏览器崩溃、卡顿的现象。而近年来前端技术的发展突飞猛进,其计算能力大大提升,由此带来其实现的可能性,而且也已经出现可用插件。本文探讨基leaflet为WEBGIS框架,实现多边形切割插件。
1 切割思路及其算法
1.1 构造初始切割对象
参照leaflet官方自带的插件,采用面向对象的思维来构建切割类CutPolygon。CutPolygon继承于leaflet的核心类Hander,代码如下:
var CutPolygon=L.Handler.extend({
includes: L.Mixin.Events,
options: {},
initialize: function (map, options) {
L.Handler.prototype.initialize.call(this, map);
L.setOptions(this, options);
this._map=map;
this._layerGroup=new L.FeatureGroup();
this._map.addLayer(this._layerGroup);
},
在CutPolygon中首先includes使切割對象赋予事件委托的方法。然后再CutPolygon的初始化方法中完成以下几步:
⑴ 首先执行hander的初始化方法;
⑵ 传入map地图对象以及其他属性;
⑶ Cutpolygon的_map属性对象指向map参数;
⑷ 声明一个图层_layerGroup并添加进map去;
1.2 鼠标捕捉
该插件初始化使用时提供鼠标捕捉多边形上的点。多边形的本质为由大于等于一条线段所组成的线段首位衔接的不闭合的折线。(注:首尾衔接的不闭合为多边形,直线为折线的特殊情况)。所以求解点到多边形的距离的问题就转化为求解点到每个线段的距离。求解思路如下。
如图1、图2所示。标注:线段l;偏离距离H;匹配的结果坐标点P0;鼠标点p及p到l的距离h;线段两个端点A1、A2;线段所在直线l1;过线段两端点做线段的法线p1、p2;P1、p2与线段所成内夹角分别为a,b;P到A1和A2的距离分别为d1,d2;
以上是针对单条线段进行的判断,在一个多边形中有n条线段,则对n条线段按照以上计算方式得到共n个捕捉匹配点,对集合Zn求出最小值Hmin作为当前位置p相对多边形的捕捉点。
1.3 切割算法
多边形按简易程度又可分为凹多边形、凸多边形以及带岛状内环的多边形。由于凸多边形可以看作是凹多边形的特例,所以本文以凹多边形为代表的任意多边形来实现切割算法,针对带岛状内环的多边形我们进行搭桥无岛化处理生成新的凹多边形来进行切割[1-2](无岛化将在另外一篇文章讲解)。
1.3.1 存储构造函数
为了数据便于管理,代码开始先定义一个构造函数用于存储交点以及多边形顶点。该构造函数参数为:距离,维度,经度,在多变行所在的索引,多边形。具体的代码参考如下(注:当不要距离参数时可默认设置为0):
1.3.2 切割步骤
先构建实例多边形P,由点P0,P1,…,P10组成。显而易见,多边形P是一个标准的凹多边形,切割算法将以多边形P为例进行说明,如图3所示。
通过鼠标双击拾取多边形上一点a0,并将该多边形存储为FOCUSON焦点多边形数据集中(注:若鼠标处于某一个多边形内该多边形自动存储为FOCUSON焦点多边形数据集中,若在多边形外则把_layerGroup图层中所有的多边形都加入FOCUSON焦点多边形数据集中,鼠标对所有多边形计算距离,取最近点为拾取点,切割线第一点确定后自动将点所在多边形确定为FOCUSON数据集中惟一的多边形)。
鼠标自动在FOCUSON的多边形上拾取第二点a5(注:需判断a5在多边形P中的索引是否等于a0的索引,防止处于同边的情况),拾取的点与第一点构建一条线段Polyline,随着鼠标移动,Polyline也动态地改变。具体按照以下步骤。
⑴ 首先判断交点数是否大于2,若等于2则计算以两个交点作为端点的线段的中点,计算此中点是否处于多边形P内,处于执行⑵,反之结束,多边形P没有切割,切割无效[3]。
⑵ 将a与多边形P相交的交点按照交点所在线段的索引排序,按照上图排序为[4]。
⑶ 按照⑵的索引排序,以切割线端点为节点将⑵结果分成两部分(a0→a5;a5→a0)。
⑷ 设a0与a5交点所在多边形P边的索引分别为Index0和Index5,针对线段a与多边形P切割的结果可分为以下两种情况[5]:
情况一:若⑵的结果a0→a5是相临两点没有中间交点,那么在多边形P的坐标点P0,P1,…,P10中,寻找处于(Index0+1,Index5)范围内所有的点,如图3所示可得
⑴
ZN中的坐标点满足要求,按照索引顺序判断a5→a0之间两两相临交点的中心点是否处于多边形P内,若结果为正确的话则不取处于相临两交点之间的多边形顶点,反之取多边形的顶点,并按照索引顺序保存。如图3所示,a5→a0方向判断的结果为:
⑵
最后将⑴的Wn拼接于⑵的Zn的尾部形成:
⑶
式⑶中的Points首尾闭合形成的多边形就是切割后的结果之一。
情况二:若⑵的结果a5→a0方向的有多个交点。按照索引顺序判断a5→a0之间两两相临交点的中心点是否处于多边形P内,若结果为否的话则按照索引顺序保存相临两交点及处于两交点之间的多边形的顶点,保存坐标将构成切割多边形的结果之一,反之不做处理。循环遍历每相临两点处理得到结果保存如上图所示处理结果为:
⑷
情况一和情况二都需要注意一种特殊情况,当多边形的起点P0处于某一个切割多边形内时即P0处于处于两个交点之间区域时,点的索引排序则需要单独处理。
1.3.3 多边形切割流程图
CutPolygon对象只提供对外调用接口,使用者并不需要了解内部如何处理。在此简单梳理使用时的内部流程图。在声明CutPolygon对象后,先进行添加需要切割的多边形,然后初始化属性参数,CutPolygon对象内部初始化事件,然后通过鼠标移动事件mousemove和鼠标双击事件dblclick来完成切割。CutPolygon内部还提供自身调用的私有函数,如:判断点是否在多边形内,WGS84坐标转Web墨卡托坐标,根据索引排序,删除图层所有要素,切割完删除被切割的多边形,CutPolygon数据更新,事件的注册和注销以及更新等[6]。以下为简化的流程图。
2 实现效果
2.1 调用参考
在了解原理之后该如何调用写好的基于leaflet的开发的多边形切割的插件呢?在前端代码中,调用leaflet并声明map之后。新建CutPolygon对象,在构件需要切割的多边形的时候导入CutPolygon对象中,然后初始化一下事件,即可调用。本文参照官方插件模式,也通过enbale开启切割,通过disable/ESC来关闭切割功能。以下为部分调用代码:
3 结论
经过以上方法设计出来的功能,在某公司研发的国土调查宗地管理系统得到应用,基本满足需求。在未来的发展中,这些基本功能需求是远远不够的,而且缓冲区,空间要素的拓扑关系判断等等也需不断适应新需求。针对本文而言,该方法切割还缺少针对大量空间要素的一次性裁剪,这样可以实现批量裁剪,节省大量时间以及多要素裁剪的同步性。这些问题在后期需要继续完善。
参考文献(References):
[1] 吴信才.地理信息系统原理与方法[M].电子工业出版社,
2002.
[2] 周培德.计算几何[M].清华大学出版社,2000.
[3] 陈瑞卿,周健,虞烈.一种判断点与多边形关系的快速算法[J].
西安交通大学学报,2007.41(1):59-63
[4] 刘强,陈玉健.论两条直线段的求交[J].计算机学报,1997.20
(12):1119-1123
[5] 田光,谢忠,吴亮.基于简单要素模型的多边形分割算法[J].地
理与地理信息科学,2010.26(1):24-28
[6] 孙小淋.基于JavaScript的消息管理机制探讨[J].軟件,
2013.7.