当前位置: 首页 > news >正文

Opnelayers:向某个方向平移指定的距离

一、需求分析

我想要封装一个平移方法,这个方法的功能是可以让视图向某个方向平移一定的距离。除了基础的能力外,我还希望有以下的功能:

  1. 可以自定义动画持续时间
  2. 可以自定义平移结束后的回调
  3. 可以自定义移动的方向,如:上、下、左、右等。
  4. 可以自定义移动距离的单位,如:米、像素等。

二、思路梳理

首先肯定还是基于veiw.animate()方法实现平移的功能。因此就需要计算出平移后的视图中心点坐标。

接下来就需要解决以下的几个问题:

① 如何根据平移的距离和平移的方向计算我们目标位置的坐标?

这个很简单,其实就是坐标正算

坐标正算是指根据已知点的坐标、已知边长及该边的坐标方位角,计算未知点的坐标。简单来说,就是已知起点坐标、两点间的距离和方位角,求终点坐标。

它的公式如下:


② 如何实现自定义移动方向?

移动方向本质上就是方位角,我可以预设一些固定的方位值(例如,向上是0度角)也以一个数字作为方位角。

‘top’  -   表示 0° 方位角

180  -  表示 180° 方位角


③ 如何兼容多种距离单位?

想要兼容多种距离单位就需要在计算目标点坐标时进行单位转换,统一各个数值的单位。我计划平移方法支持 米、千米、像素 三种单位,并最终全部转换为像素单位。转换的方法如下:

像素 = 米 / 分辨率

像素 = ( 千米 * 1000 ) / 分辨率

因为分辨率就表示图上一个像素所对应的实地距离,因此可以利用分辨率将移动距离由米转换为像素。


具体的实现步骤如下:

  1. 获取平移起点的坐标(即视图中心点坐标)
  2. 将起点坐标由地理空间坐标(以米或度为单位)转换为屏幕坐标(以像素为单位)
  3. 将移动距离的单位全部转换为像素
  4. 计算平移的方位角
  5. 根据起点坐标、平移距离、方位角计算终点距离
  6. 将终点坐标由屏幕坐标(以像素为单位)转换为地理空间坐标(以米或度为单位)
  7. 执行veiw.animate()方法

三、成果展示

平移方法

 

/*** @abstract 向某个方向平移一段距离* @param {*} map* @param {number} distance 要平移的距离* @param { 'top' | 'bottom' | 'left' | 'right'  | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | number } direction 要平移的方向  如果参数为一个数字则表示方位角(以度为单位)* @param { 'px' | 'm' | 'km' } unit 距离的单位* @param {number} duration 动画持续时间* @param {function} done 动画结束后的回调函数*/
export const pan = (map,{ distance, direction, unit = "m", duration = 1000 },done = () => {}
) => {// 获取当前视图中心点坐标const view = map.getView();const center = view.getCenter();const resolution = view.getResolution();// 将中心点坐标转换为像素坐标const centerPixel = map.getPixelFromCoordinate(center);// 将需要移动的距离转换为像素距离let finalDistance = distance;if (unit === "km") {finalDistance =(distance * 1000) /(resolution * view.getProjection().getMetersPerUnit());} else if (unit === "m") {finalDistance =distance / (resolution * view.getProjection().getMetersPerUnit());}// 计算方位角let alpha;if (typeof direction === "number") {alpha = direction * (Math.PI / 180);} else {switch (direction) {case "top":alpha = 270 * (Math.PI / 180);break;case "bottom":alpha = 90 * (Math.PI / 180);break;case "left":alpha = 180 * (Math.PI / 180);break;case "right":alpha = 0;break;case "top-left":alpha = 225 * (Math.PI / 180);break;case "top-right":alpha = 135 * (Math.PI / 180);break;case "bottom-left":alpha = 315 * (Math.PI / 180);break;case "bottom-right":alpha = 45 * (Math.PI / 180);break;default:return;}}// 通过坐标正算计算平移后的中心点坐标let newCenterPixel = [];newCenterPixel[0] = centerPixel[0] + finalDistance * Math.cos(alpha);newCenterPixel[1] = centerPixel[1] + finalDistance * Math.sin(alpha);// 将新的中心点坐标转换为地理坐标const newCenter = map.getCoordinateFromPixel(newCenterPixel);// 执行平移动画view.animate({center: newCenter,duration: duration,},done);
};

四、要点分析

1.空间坐标与屏幕坐标的转换

在OpenLayers中可以通过内置的方法实现空间坐标与屏幕坐标的转换

view.getPixelFromCoordinate(coordinate)  // 空间坐标 => 屏幕坐标view.getCoordinateFromPixel(pixel)       // 屏幕坐标 => 空间坐标

2.注意地理坐标系统对分辨率的影响

我们需要通过分辨率来实现地理单位(km,m)与 像素单位(px)的转换。但是分辨也是会受当前地图的地理坐标系统的影响:

  • projection:'EPSG:4326' (地理坐标系),则分辨率的单位是度。
  • projection:'EPSG:3857'(投影坐标系),则分辨率的单位是米。

因此如果当前地图使用的是像'EPSG:4326'这样的地理坐标系,就需要将分辨率的单位由度转换为米,这样才能使用分辨率正确的计算出要平移的距离。

可以通过projection.getMetersPerUnit()方法来获取当前坐标系下1个单位长度所对应的米数。

因此在考虑了地理坐标系统的影响后,将平移距离的单位转换为像素的公式就可以这样写:

像素 = 米 / ( 分辨率 * 当前坐标系每单位对应米数 )

像素 = ( 千米 * 1000 ) / ( 分辨率 * 当前坐标系每单位对应米数 )

对应到代码中就是这样:

// m => px
distance_px = distance_m / (resolution * view.getProjection().getMetersPerUnit());// km => px
distance_px = (distance_m * 1000) / (resolution * view.getProjection().getMetersPerUnit());

3.注意在屏幕坐标系下的角度

当我们将点和距离都转换为像素单位后,坐标就需要遵循屏幕坐标系,屏幕坐标系是以屏幕左上角为原点,向右为x周正方形,向下位y轴的正方向。

因此在计算方位角时就要以从左向右的这条线为零度线进行计算。

在屏幕坐标系下各个方向所对应的方位角如下:

方向

方位角(度)

270°

90°

180°

左上

225°

右上

315°

右下

45°

左下

135°

参考资料

  1. OpenLayers v10.5.0 API - Class: Map
  2. OpenLayers v10.5.0 API - Class: Projection
  3. Canvas学习系列二:Canvas的坐标系统 - 六小登登 - 博客园

相关文章:

  • C++初阶-模板初阶
  • 业务中台与数据中台:企业数字化转型的核心引擎
  • 【源码】【Java并发】【ConcurrentHashMap】适合中学体质的ConcurrentHashMap
  • 全球城市范围30米分辨率土地覆盖数据(1985-2020)
  • MCP协议:AI生态的统一标准
  • ppt章节页怎么做好看?ppt章节页模板
  • 京东商品详情数据爬取难度分析与解决方案
  • 线上线程池的调优与监控 - Java架构师面试实战
  • C++ 基础内容入门
  • 服务器ubuntu镜像磁盘空间怎么管理
  • Java学习--HashMap
  • Nacos简介—4.Nacos架构和原理二
  • Rabbit MQ的基础认识
  • Support for password authentication was removed on August 13, 2021
  • Linux文件操作
  • 牟乃夏《ArcGIS Engine地理信息系统开发教程》学习笔记3-地图基本操作与实战案例
  • 小黑享受思考心流: 73. 矩阵置零
  • Spring Boot中@RequestParam、@RequestBody、@PathVariable的区别与使用
  • 解读TypeScript 类型工具
  • GAMES202-高质量实时渲染(Real-Time Shadows)
  • 人民日报:上海“模速空间”何以汇聚超百家大模型企业
  • 同日哑火丢冠,双骄的下山路,手牵手一起走
  • 乌副总理:乌美签署矿产协议
  • 全国人大常委会关于授权国务院在中国(新疆)自由贸易试验区暂时调整适用《中华人民共和国种子法》有关规定的决定
  • 周劼已任中国航天科技集团有限公司董事、总经理、党组副书记
  • 200枚篆刻聚焦北京中轴线,“印记”申遗往事