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

一、需求分析

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

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

二、思路梳理

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

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

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

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

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

它的公式如下:


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

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

'top'- 表示 0° 方位角

180 - 表示 180° 方位角


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

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

像素 = 米 / 分辨率

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

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


具体的实现步骤如下:

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

三、成果展示

JavaScript 复制代码
/**
 * @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中可以通过内置的方法实现空间坐标与屏幕坐标的转换

JavaScript 复制代码
view.getPixelFromCoordinate(coordinate)  // 空间坐标 => 屏幕坐标

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

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

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

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

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

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

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

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

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

对应到代码中就是这样:

JavaScript 复制代码
// 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的坐标系统 - 六小登登 - 博客园
相关推荐
香蕉可乐荷包蛋4 小时前
浅入ES5、ES6(ES2015)、ES2023(ES14)版本对比,及使用建议---ES6就够用(个人觉得)
前端·javascript·es6
未来之窗软件服务4 小时前
资源管理器必要性———仙盟创梦IDE
前端·javascript·ide·仙盟创梦ide
liuyang___5 小时前
第一次经历项目上线
前端·typescript
西哥写代码6 小时前
基于cornerstone3D的dicom影像浏览器 第十八章 自定义序列自动播放条
前端·javascript·vue
清风细雨_林木木6 小时前
Vue 中生成源码映射文件,配置 map
前端·javascript·vue.js
FungLeo6 小时前
node 后端和浏览器前端,有关 RSA 非对称加密的完整实践, 前后端匹配的代码演示
前端·非对称加密·rsa 加密·node 后端
雪芽蓝域zzs6 小时前
JavaScript splice() 方法
开发语言·javascript·ecmascript
不灭锦鲤6 小时前
xss-labs靶场第11-14关基础详解
前端·xss
不是吧这都有重名7 小时前
利用systemd启动部署在服务器上的web应用
运维·服务器·前端
霸王蟹7 小时前
React中巧妙使用异步组件Suspense优化页面性能。
前端·笔记·学习·react.js·前端框架