最近產(chǎn)品提出一個(gè)需求,在我們使用的騰訊地圖上為線路polyline
添加線路方向。例如下圖所示:
查找騰訊地圖JS API提供的API,沒(méi)有找到對(duì)應(yīng)的支持,詢問(wèn)負(fù)責(zé)騰訊地圖的人也得到了同樣的答案,即地圖JS API不支持線路畫(huà)方向。于是否就利用地圖的Marker
類配合旋轉(zhuǎn)來(lái)實(shí)現(xiàn)這個(gè)功能。
實(shí)現(xiàn)原理
因?yàn)槭抢?code style="margin: 1px 5px; padding: 0px 5px !important; line-height: 1.8; vertical-align: middle; display: inline-block; font-family: "Courier New", sans-serif !important; font-size: 12px !important; background-color: rgb(245, 245, 245) !important; border: 1px solid rgb(204, 204, 204) !important; border-radius: 3px !important;">Marker來(lái)實(shí)現(xiàn)Polyline
帶方向箭頭功能,所以要根據(jù)線路不同局部的具體走向來(lái)旋轉(zhuǎn)Marker的Icon,從而實(shí)現(xiàn)該功能。
另外,我們需要知道:
Marker
的旋轉(zhuǎn)方向是跟時(shí)針?lè)较虮3忠恢碌?,角度為正表示順時(shí)針旋轉(zhuǎn),負(fù)表示逆時(shí)針旋轉(zhuǎn)。
騰訊地圖的JS API雖然沒(méi)有提供畫(huà)箭頭的支持,但是可喜的是,騰訊地圖提供了一個(gè)類qq.maps.geometry.spherical
,它提供了一些方法用于計(jì)算面積、角度和距離,具體可以參考這里。
其中,對(duì)于我們實(shí)現(xiàn)方向箭頭有用的是以下兩個(gè)api:
computeHeading(from:LatLng, to:LatLng)
: 返回從一個(gè)坐標(biāo)到另一個(gè)坐標(biāo)的航向。航向是指從一個(gè)坐標(biāo)指向另一個(gè)坐標(biāo)的向量與正北方向的夾角,范圍為[-180,180)。computeDistanceBetween(from:LatLng, to:LatLng, radius?:Number)
: 返回兩坐標(biāo)點(diǎn)間的距離。
結(jié)合上面所描述的,具體的實(shí)現(xiàn)原理圖如下圖展示:
具體實(shí)現(xiàn)步驟:
利用
computeHeading
方法計(jì)算航向,然后由其計(jì)算Marker
旋轉(zhuǎn)的角度。
注意:
由航向計(jì)算
Marker
旋轉(zhuǎn)角度,需要根據(jù)具體的Marker的Icon圖形來(lái)具體分析,不能一概而論。比如本人項(xiàng)目使用的Marker icon圖為水平方向的箭頭,如下圖:
那么,根據(jù)該icon圖可以計(jì)算對(duì)應(yīng)的marker旋轉(zhuǎn)角度,具體計(jì)算規(guī)則如下圖所示。其它方向的Icon可以推算出對(duì)應(yīng)的計(jì)算規(guī)則。
利用
computeDistanceBetween
方法計(jì)算兩坐標(biāo)點(diǎn)中間位置的經(jīng)緯度創(chuàng)建Marker實(shí)例,并設(shè)置其Icon和旋轉(zhuǎn)角度
實(shí)現(xiàn)代碼
正如上面描述的實(shí)現(xiàn)原理,下面即是實(shí)現(xiàn)為Polyline
實(shí)例添加方向箭頭Marker的實(shí)現(xiàn)代碼:
function setIcon(marker){ var size = new qq.maps.Size(9, 8); //marker icon圖片大小為18px * 16px, 等比例縮放 var anchor = new qq.maps.Point(5, 4); //經(jīng)緯度點(diǎn)在圖標(biāo)中的位置點(diǎn) var image = require('imgs/arrow.png'); var icon = new qq.maps.MarkerImage(image, size, undefined, anchor, size); marker.setIcon(icon); }//畫(huà)markerfunction addMarkers(lat, lng, opts){ var position = new qq.maps.LatLng(lat, lng); var defaultOps = { map: mapInstance, //mapInstance為對(duì)應(yīng)的qq map實(shí)例 position, zIndex: 8, visible: true, draggable: false } var options = Object.assign({}, defaultOpts, opts || {}); var marker = new qq.maps.Marker(options); setIcon(marker); return marker}//計(jì)算線路方向箭頭旋轉(zhuǎn)的方向,heading為兩個(gè)經(jīng)緯度點(diǎn)之間的航向(兩點(diǎn)之間與正北方向的夾角)//其范圍為[-180, 180)function computeRotaion(heading){ let rotation; switch(true){ case heading < -90: rotation = 270 + heading; break; case heading >= -90 && heading < 0 : rotation = -(heading - 90); break; case heading >= 0 && heading <= 90 || heading > 90: rotation = heading - 90; break; } return rotation}//為polyline添加方向markerfunction addArrowMarkers(polyline){ var defaultOps = { cursor: 'normal', zIndex: polyline.getZIndex() + 1, clickable: false, draggable: false }; var linePoint = polyline.getPath();//線的經(jīng)緯度坐標(biāo) var arrowCount= linePoint.length; for(let i = 1; i < arrowCount; i+=2){//不是每?jī)蓚€(gè)點(diǎn)之間都畫(huà)箭頭,而是每隔一個(gè)間隔畫(huà)一個(gè)箭頭 let pixelStart = linePoint.getAt(i-1); let pixelEnd = linePoint.getAt(i); let rotation, arrowLatLng, marker; let spherical = qq.maps.geometry.spherical; let distance = spherical.computeDistanceBetween(pixelStart, pixelEnd); //計(jì)算兩經(jīng)緯度坐標(biāo)件的距離 if(distance <= 15) {//距離太近小于15m的兩經(jīng)緯度坐標(biāo)點(diǎn)間不畫(huà)方向 continue; } rotation = spherical.computeHeading(pixelStart, pixelEnd);//兩經(jīng)緯度坐標(biāo)點(diǎn)之間的航向 //計(jì)算兩經(jīng)緯度坐標(biāo)點(diǎn)中間位置的經(jīng)緯度 arrowLatLng = spherical.computeOffsetOrigin(pixelEnd, distance/2, rotation); marker = addMarker(arrowLatLng.lat, arrowLatLng.lng, defaultOps); let heading = computeRotaion(rotation); //由兩坐標(biāo)點(diǎn)之間的航向計(jì)算marker要旋轉(zhuǎn)的角度 marker.setRotation(heading); } }
至此,帶方向的polyline線路就帶有方向箭頭了,可以很清晰的看出線路的走向了。
分類: 其他
http://www.cnblogs.com/wonyun/p/7152017.html