Opnelayers:向某个方向平移指定的距离
一、需求分析
我想要封装一个平移方法,这个方法的功能是可以让视图向某个方向平移一定的距离。除了基础的能力外,我还希望有以下的功能:
- 可以自定义动画持续时间
- 可以自定义平移结束后的回调
- 可以自定义移动的方向,如:上、下、左、右等。
- 可以自定义移动距离的单位,如:米、像素等。
二、思路梳理
首先肯定还是基于veiw.animate()
方法实现平移的功能。因此就需要计算出平移后的视图中心点坐标。
接下来就需要解决以下的几个问题:
① 如何根据平移的距离和平移的方向计算我们目标位置的坐标?
这个很简单,其实就是坐标正算。
坐标正算是指根据已知点的坐标、已知边长及该边的坐标方位角,计算未知点的坐标。简单来说,就是已知起点坐标、两点间的距离和方位角,求终点坐标。
它的公式如下:
② 如何实现自定义移动方向?
移动方向本质上就是方位角,我可以预设一些固定的方位值(例如,向上是0度角)也以一个数字作为方位角。
‘top’ - 表示 0° 方位角
180 - 表示 180° 方位角
③ 如何兼容多种距离单位?
想要兼容多种距离单位就需要在计算目标点坐标时进行单位转换,统一各个数值的单位。我计划平移方法支持 米、千米、像素 三种单位,并最终全部转换为像素单位。转换的方法如下:
像素 = 米 / 分辨率
像素 = ( 千米 * 1000 ) / 分辨率
因为分辨率就表示图上一个像素所对应的实地距离,因此可以利用分辨率将移动距离由米转换为像素。
具体的实现步骤如下:
- 获取平移起点的坐标(即视图中心点坐标)
- 将起点坐标由地理空间坐标(以米或度为单位)转换为屏幕坐标(以像素为单位)
- 将移动距离的单位全部转换为像素
- 计算平移的方位角
- 根据起点坐标、平移距离、方位角计算终点距离
- 将终点坐标由屏幕坐标(以像素为单位)转换为地理空间坐标(以米或度为单位)
- 执行
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° |
右 | 0° |
左上 | 225° |
右上 | 315° |
右下 | 45° |
左下 | 135° |
参考资料
- OpenLayers v10.5.0 API - Class: Map
- OpenLayers v10.5.0 API - Class: Projection
- Canvas学习系列二:Canvas的坐标系统 - 六小登登 - 博客园