一 前言

  近期翻阅博客,看到社区大神一休哥的一篇《canvas 奇巧淫技(二)绘制箭头路径效果》文章,同样,该大神还展示过一个使用rbush库如何在前端快速从海量数据进行空间检索的案例:https://alex2wong.github.io/mapbox-plugins/examples/rbush/,很有分享精神的前端GIS专家,更多关于前端GIS检索数据的技术可参考搜狐的干货专访:《深入理解空间搜索算法 ——数百万数据中的瞬时搜索》。关于轨迹样式带导航箭头这种常见问题,笔者基于兴趣和朋友们的总结,也试着用熟悉的OpenLayers的StyleFunction去实现一个这样的玩具,在此分享给大家。

高德轨迹箭头.png

  基于已知的一条轨迹,实现这样的一个导航轨迹箭头,需要解决三个问题:

  • 在轨迹上根据固定像素间隔,计算当前地图分辨率下箭头总数量。
  • 计算当前地图分辨率下,每个箭头的绘制位置。
  • 计算好箭头的数量和位置后,要确定箭头的方向。

一 箭头数量

  由高德轨迹箭头图可知,每隔固定像素,打上一个箭头。假设当前的线LineString地理长度为length,当前固定像素间隔stpes=n像素,在当前地图比例尺res已知的情况下,n像素地理距离是resn,那么箭头总数count=length/(resn):

let length=line_geom.getLength();//线图形的地理长度
const steps=40;//每隔40像素打一个箭头点
let geo_steps=map_res*steps;//40像素长度在当前地图比例尺下地理长度。
let arrow_count=length*1.0/geo_steps;

多么浅显易懂的道理啊,第一个问题很顺利的解决了。

二 箭头位置

  第一步得到了箭头的总数,在获取箭头位置时,一个重要的API是线条LineString的getCoordinateAt,利用它我们在轨迹线上获取箭头点的位置。

/*
fraction:参考点的百分比,如0就是LineString的起点,1就是LineString的终点,0.5就是LineString的中点。
*/
linestring.getCoordinateAt(fraction, opt_dest)

  假如箭头总数为arrowsNum,那么arrowsNum个箭头的数量分别是

 for(let i=1;i<arrowsNum;i++){let arraw_coor=geometry.getCoordinateAt(i*1.0/arrowsNum);console.log(arraw_coor);//输出每个箭头的坐标}

  得到每个箭头的位置后,我们先可视化下吧,OpenLayers的地图样式完全由StyleFunction实现的,完整样式代码如下:

/*
feature:地图上的要素对象,既有属性,也有坐标图形。
res:当前地图分辨率参数。
return:返回一个定制的渲染样式
*/
var styleFunction = function(feature,res){//轨迹线图形var trackLine= feature.getGeometry();var styles = [new ol.style.Style({stroke: new ol.style.Stroke({color: '#2E8B57',width: 10})})];//轨迹地理长度let length=trackLine.getLength();//像素间隔步长let stpes=40;//像素步长间隔//将像素步长转实际地理距离步长let geo_steps=stpes*res;//箭头总数let arrowsNum=parseInt(length/geo_steps);for(let i=1;i<arrowsNum;i++){let arraw_coor=trackLine.getCoordinateAt(i*1.0/arrowsNum);styles.push(new ol.style.Style({geometry: new ol.geom.Point(arraw_coor),image: new ol.style.Circle({radius: 7,fill: new ol.style.Fill({color: '#ffcc33'})})}));}return styles;
}
箭头位置计算与可视化结果.png

三 箭头方向

  之前的逻辑,我们已经计算了一个轨迹样式的雏形了,把地图上箭头位置的黄点改成一个箭头图标,做下方向旋转就可以了。在说明此前,需要说明下轨迹,segment线段,箭头点之间的关系,如下图:

轨迹,segment,箭头位置之间的关系.png

  观察示意图,总结如下:

  • 一条完整的轨迹由多个连续的segment组成。
  • 通过getCoordinateAt方法计算得到的箭头点,一定是在轨迹线上的某个点。
  • 每个箭头点的方向是由箭头点落在的segment的方向决定的。
    很显然,计算箭头方向其实就是计算每个箭头点到底落在了哪个segment上,将segme方向赋予箭头点。这里我们引入了rbush库构建空间索引,计算轨迹点与segment对应关系。
      之所以我要引入rbush库,是解决循环计算问题,想象下如果不引入rbush库,只能使用如下的伪代码暴力计算了:
for(let i=0;i<arrows.length;i++){for(let j=0;j<segments.length;j++){if(instersects(arrows[i],segments[j])===true){// arrows[i]对应的segments是segments[j]break;}}
}

感觉逻辑很简单啊,这样做难道不可以吗?想象下,箭头数量,segment的数量其实都是不可控的,一个复杂的轨迹线可能由成百上千的近万的segments,这样一个个循环去匹配,效率是不是就有问题了?所以引入了空间索引。这里查询,使用了rbush进行btree查询,查询的结果后再详细比对是否和箭头相交,累了,直接贴代码了,不详述了:

 var styleFunction = function(feature,res){//轨迹线图形var trackLine= feature.getGeometry();var styles = [new ol.style.Style({stroke: new ol.style.Stroke({color: '#2E8B57',width: 10})})];//对segments建立btree索引let tree= rbush();//路段数trackLine.forEachSegment(function(start, end) {var dx = end[0] - start[0];var dy = end[1] - start[1];//计算每个segment的方向,即箭头旋转方向let rotation = Math.atan2(dy, dx);let geom=new ol.geom.LineString([start,end]);let extent=geom.getExtent();var item = {minX: extent[0],minY: extent[1],maxX: extent[2],maxY: extent[3],geom: geom,rotation:rotation};tree.insert(item);});//轨迹地理长度let length=trackLine.getLength();//像素间隔步长let stpes=40;//像素步长间隔//将像素步长转实际地理距离步长let geo_steps=stpes*res;//箭头总数let arrowsNum=parseInt(length/geo_steps);for(let i=1;i<arrowsNum;i++){let arraw_coor=trackLine.getCoordinateAt(i*1.0/arrowsNum);let tol=10;//查询设置的点的容差,测试地图单位是米。如果是4326坐标系单位为度的话,改成0.0001.let arraw_coor_buffer=[arraw_coor[0]-tol,arraw_coor[1]-tol,arraw_coor[0]+tol,arraw_coor[1]+tol];//进行btree查询var treeSearch = tree.search({minX: arraw_coor_buffer[0],minY: arraw_coor_buffer[1],maxX: arraw_coor_buffer[2],maxY: arraw_coor_buffer[3]});let arrow_rotation;//只查询一个,那么肯定是它了,直接返回if(treeSearch.length==1)arrow_rotation=treeSearch[0].rotation;else if(treeSearch.length>1){let results=treeSearch.filter(function(item){//箭头点与segment相交,返回结果。该方法实测不是很准,可能是计算中间结果//保存到小数精度导致查询有点问题// if(item.geom.intersectsCoordinate(arraw_coor))//   return true;//换一种方案,设置一个稍小的容差,消除精度问题let _tol=1;//消除精度误差的容差if(item.geom.intersectsExtent([arraw_coor[0]-_tol,arraw_coor[1]-_tol,arraw_coor[0]+_tol,arraw_coor[1]+_tol]))return true;})if(results.length>0)arrow_rotation=results[0].rotation;}styles.push(new ol.style.Style({geometry: new ol.geom.Point(arraw_coor),image: new ol.style.Icon({src: '../static/content/images/arrowright.png',anchor: [0.75, 0.5],rotateWithView: true,rotation: -arrow_rotation})}));}return styles;}
轨迹箭头效果图.png

看着还凑合吧,但其实要做到高德那个精细的样式,才万里第一步,祝诸君继续研究,期待更好的效果。

基于OpenLayers+rbush实现高德轨迹样式相关推荐

  1. (转)基于openlayers实现聚类统计展示

    http://blog.csdn.net/gisshixisheng/article/details/46137015 概述: 在前面的博文中讲述过基于Arcgis for js如何实现聚类统计展示, ...

  2. 【WebGIS】二、基于Openlayers实现地图的加载与显示

    二.基于Openlayers实现地图的加载与显示 基于上文中配置好的环境,先通过Element Plus实现一个页面的布局,然后基于openlayers加载显示全球瓦片地图. 1. 引入element ...

  3. 基于openlayers实现聚类统计展示

    概述: 在前面的博文中讲述过基于Arcgis for js如何实现聚类统计展示,在本文中讲述如何基于openlayers实现聚类统计的效果,Arcgis for js聚类统计的博文地址为: http: ...

  4. Vue + 高德轨迹播放(一)

    目录 想法由来 地图 创建项目 必要依赖项 引入高德地图key 页面中使用 启动过程中的错误和解决 参考高德官方示例 数据准备 方案一 接入效果 总结 想法由来 前不久项目上要用到地图的轨迹播放功能, ...

  5. 顶会论文:基于神经网络StarNet的行人轨迹交互预测算法

    1.背景 民以食为天,如何提升超大规模配送网络的整体配送效率,改善数亿消费者在"吃"方面的体验,是一项极具挑战的技术难题.面向未来,美团正在积极研发无人配送机器人,建立无人配送开放 ...

  6. 孪生神经网络_驾驶习惯也能识人?基于时空孪生神经网络的轨迹识别

    ⬆⬆⬆ 点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入! 前言: 给定一组单独的人员(例如行人,出租车司机)的历史轨迹以及由特定人员生成的一组新轨迹,轨迹识别问题旨在验证传入的轨迹是否是 ...

  7. 基于阻抗控制的工业机器人轨迹跟踪系统 Simulink/Simscape 仿真

      本文是对文献 [1] 的复现. 文章目录 更新日志 关于阻抗控制 离散化阻抗控制控制器的实现 基于阻抗控制的工业机器人轨迹跟踪系统仿真 ■ 环境说明 ■ 系统说明 ■ 结果展示 后记 项目开源 参 ...

  8. matlab link offset,基于MATLAB教学型机器人空间轨迹仿真

    基于MATLAB教学型机器人空间轨迹仿真 robotic toolbox for matlab工具箱 1. PUMA560的MATLAB仿真 要建立PUMA560的机器人对象,首先我们要了解PUMA5 ...

  9. vue openlayers 加载高德地图等 gcj02 的图层偏移问题

    vue openlayers 加载高德地图等 gcj02 的图层偏移问题 这个问题是在使用 openlayers 地图引擎加载高德地图或者是谷歌地图都会遇到的问题,所以说呢这篇博文稍微说一下解决办法. ...

最新文章

  1. 高频面试题:秒杀场景设计
  2. SAP QM初阶之明明存在检验计划但是生成的检验批取不到?
  3. 35岁的程序员是“都挺好”还是“都挺惨”?\n
  4. 我的Oracle 9i学习日志(18)-- 维护数据完整性.b
  5. 用Maven新建Web项目时报错
  6. z-index无效解决
  7. Flutter- Android项目集成flutter模块
  8. 各领域公开数据集下载
  9. tailwindcss:弟弟们都往后稍稍
  10. js实现数字的千分化
  11. Linux 下固态硬盘恢复误删除文件
  12. Solution to no ADO.NET in VS2019 VS里没有ADO的解决办法
  13. 直方图的计算,绘制与分析
  14. yum一次性下载安装包及其依赖包
  15. 不死的LYM NOIP模拟 二分+状压DP
  16. MFC CPropertySheet 多页面切换 实例 .
  17. vant中uploader上传图片
  18. 电脑护眼软件Mac版本和Windows
  19. 计算机在线安装,韩博士在线一键重装系统教程
  20. PS 自定义面板 工作区

热门文章

  1. 关于深度残差收缩网络,你需要知道这几点
  2. 性能不打折,内存占用减少90%,Facebook提出极致模型压缩方法Quant-Noise
  3. SAP 如何将无序列号的库存与序列号关联起来?
  4. 一位人工智能总监对AI行业的【实话实说】
  5. 顶会「扩招」,一地鸡毛:ICLR2020近半数审稿人未发过相关论文
  6. 一禁了之还是放开应用?面部识别“人红是非多”
  7. 人脸识别屡遭非议 会成为“潘多拉魔盒”吗
  8. 演技之神周润发背后的故事——评影帝周润发鲜为人知的封神之路
  9. 加速!上海要做人工智能产业“领头雁”
  10. 项目经理生存现状(漫画)