王強+雷中英


摘 要: 根據開發人員對多邊形進行空間操作的需求,研究在開源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.