使用openlayers绘制类似高德的带方向箭头导航线(二)

使用openlayers绘制类似高德的带方向箭头导航线(二)

前言

上一篇博客中将绘制导航线的思路及实现与大家分享,然后留了个性能的问题,在这篇文章中解决;首先我们得知道性能问题的根源,是因为随着级别的放大,要计算线上的所有标记点的,这个数量是成几何倍数增加的,因此我们可以从只计算屏幕范围内的点这个思路触发解决。

前期准备

这次我们引入一个新的三方库,熟悉gis的朋友应该都使用过 turf.js ,turf.js是一个非常强大的处理空间数据的框架,有兴趣的朋友可以自己了解下,地址:http://turfjs.org/docs/#

npm安装turf.js
  1. npm安装 cnpm install @turf/turf -save
  2. 引用 import * as turf from ‘@turf/turf’

实现思路

  • 获取地图当前视角的extent;
  • 使用turf.js中的API,turf.bboxClip(line_,extent),将当前线与地图视角相切,获取视野内的被切的线的集合;
  • 然后使用上一章的方法,计算每条线上的标记点及旋转方向;

关键代码分解

通过注释将关键代码每一步分解;

//获取当前视野范围
let extent_ = this.getPointExtent(1);
//判断当前是否级别发生变化,当级别变化的大于0.5时,重新计算
//判断当前视野范围是否在计算的1.5倍视野范围内,
//在范围内不用继续计算,还是使用之前计算的piont
//不在范围内的时候计算point
if(!this.zoomChanged() && this.points_extent[0] < extent_[0] && this.points_extent[1] < extent_[1] && extent_[2] < this.points_extent[2] && extent_[3] < this.points_extent[3]){
	return
}
//获取当前1.5倍视野范围
this.points_extent = this.getPointExtent(1.5);
this.olSource_point.clear();
let this_ = this;
//获取线的顶点
let coords = this_.geo_line.getCoordinates();
//使用1.5倍范围对线进行切割
let line_ = turf.lineString(coords);
let line_clip_arr = turf.bboxClip(line_,this.points_extent);
//切完后的线若还是一条线,则geometry.type为'LineString';若为多条线,则为"MultiLineString";
if(line_clip_arr && line_clip_arr.geometry){
	if(line_clip_arr.geometry.type == "LineString"){
		//直接获取标记点
		this_.getPointsByLine(line_clip_arr.geometry.coordinates)
	}else if(line_clip_arr.geometry.type == "MultiLineString"){
		//通过for循环获取标记点
		line_clip_arr.geometry.coordinates.forEach(coords=>{
			this_.getPointsByLine(coords)
		})
	}
}

完整代码

import Point from 'ol/geom/Point';
import LineString from 'ol/geom/LineString';
import Feature from 'ol/Feature';
import {Icon, Stroke, Style} from 'ol/style';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import {fromLonLat} from 'ol/proj';
import {getUid} from 'ol/util';
import * as turf from '@turf/turf';
class NavigationLine {
	constructor(opt,map){
		this.id = opt.id ? opt.id : getUid(opt);
		this.default_style_opt = {
			line_width:5,
			line_stroke:'#459c50',
			interval:40,
		}
		this.style = Object.assign({},this.default_style_opt,opt.style);
		this.olMap = map;
		this.datas = opt.datas;
		this.points_extent = new Array(4);
		this.viewZoom = map.getView().getZoom();
		this.init();
		this.addData()
		map.getView().on("change",this.getPoints.bind(this))
	}
	init(){
		this.olSource_line = new VectorSource();
		this.olLayer_line = new VectorLayer({
			source:this.olSource_line,
			style: ()=>{
				return new Style({
					stroke: new Stroke({
						color: this.style.line_stroke,
						width: this.style.line_width,
					}),
				})
			},
		})
		this.olSource_point = new VectorSource();
		this.olLayer_point = new VectorLayer({
			source:this.olSource_point,
			style: (feature)=>{
				let rotation = feature.get('rotation_');
				return new Style({
					image: new Icon({
						src: 'img/arrow1.png',
						rotateWithView: true,
						rotation: Math.PI + rotation,
						scale:this.style.line_width/12,
						// imgSize:[this.style.line_width,this.style.line_width]
					}),
				})
			}
		})
		this.olMap.addLayer(this.olLayer_line);
		this.olMap.addLayer(this.olLayer_point);
	}
	addData(){
		//添加线
		let coords = this.datas.map(data_ => {
			return fromLonLat([data_.lon,data_.lat])
		});
		this.geo_line = new LineString(coords)
		let fea_line = new Feature({
			geometry:this.geo_line,
		});
		this.olSource_line.addFeatures([fea_line]);
		this.getPoints()
	}
	getPoints(e){
		if(!this.olSource_point || !this.geo_line){return}
		let extent_ = this.getPointExtent(1);
		//判断当前视野范围是否在计算的1.5倍视野范围内,
		//在范围内不用继续计算,还是使用之前计算的piont
		//不在范围内的时候计算point
		if(!this.zoomChanged() && this.points_extent[0] < extent_[0] && this.points_extent[1] < extent_[1] && extent_[2] < this.points_extent[2] && extent_[3] < this.points_extent[3]){
			return
		}
		this.points_extent = this.getPointExtent(1.5);
		this.olSource_point.clear();
		let this_ = this;
		let coords = this_.geo_line.getCoordinates();

		let line_ = turf.lineString(coords);
		let line_clip_arr = turf.bboxClip(line_,this.points_extent)
		if(line_clip_arr && line_clip_arr.geometry){
			if(line_clip_arr.geometry.type == "LineString"){
				this_.getPointsByLine(line_clip_arr.geometry.coordinates)
			}else if(line_clip_arr.geometry.type == "MultiLineString"){
				line_clip_arr.geometry.coordinates.forEach(coords=>{
					this_.getPointsByLine(coords)
				})
			}
		}
	}
	getPointsByLine(coords){
		let this_ = this;
		let feas = []
		let distance_ = this_.style.interval/2;//首个点放置在距离起点1/2间隔的位置
		let pix_start = this_.olMap.getPixelFromCoordinate(coords[0])
		let pix_end
		for(let i = 1;i<coords.length;i++){
			let coord_,coord_pix;
			let fea_
			pix_end = this_.olMap.getPixelFromCoordinate(coords[i]);
			let dis_start2end = Math.sqrt(Math.pow((pix_start[0]-pix_end[0]),2)+Math.pow((pix_start[1]-pix_end[1]),2))//计算收尾在屏幕上的距离
			if(dis_start2end > distance_){//距离大于间隔
				//计算距离开始点位的像素值
				coord_pix = [
					(distance_ * (pix_end[0] - pix_start[0])) / dis_start2end + pix_start[0],
					(distance_ * (pix_end[1] - pix_start[1])) / dis_start2end + pix_start[1]
				];
				//计算经纬度
				coord_ = this_.olMap.getCoordinateFromPixel(coord_pix);
				fea_ = new Feature({
					geometry:new Point(coord_)
				})
				fea_.set('rotation_',Math.atan2(pix_end[1]-pix_start[1],pix_end[0]-pix_start[0]))
				//下次循环开始点为当前点
				pix_start = coord_pix;
				distance_ = this_.style.interval;
				i--;
			}else if(dis_start2end == distance_){//距离等于间隔
				fea_ = new Feature({
					geometry:new Point(coords[i])
				})
				fea_.set('rotation_',Math.atan2(pix_end[1]-pix_start[1],pix_end[0]-pix_start[0]))
				pix_start = pix_end;
				distance_ = this_.style.interval;
			}else{//距离小于间隔
				distance_ = distance_ - dis_start2end;
				pix_start = pix_end;
			}
			fea_ && feas.push(fea_);
		}
		this.olSource_point.addFeatures(feas);
	}
	getPointExtent(n){
		n = n ? n : 1;
		let view = this.olMap.getView();
		let mapsize = this.olMap.getSize().map(it_=>{
			return it_*n
		});
		return view.calculateExtent(mapsize);
	}
	zoomChanged(){
		let view = this.olMap.getView();	
		if( Math.abs(this.viewZoom - view.getZoom() ) >= 1 ){
			this.viewZoom = view.getZoom();
			return true
		}else{
			return false
		}
	}
}
export default NavigationLine;

后记

调用方式与实现的效果与上一篇文章一样,此文章是为了解决上一篇中的性能问题;

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值