地图事件与坐标换算
地图事件同时给出三种坐标:屏幕像素、视图坐标、浏览器事件。 用错其中任何一个,点击位置就会偏,而且偏得很稳定,容易误判成数据问题。
事件对象里的三个坐标系
| 字段 | 单位 | 含义 |
|---|---|---|
event.pixel | 像素 | 相对地图容器左上角 |
event.coordinate | 视图投影单位(默认米) | 点击位置对应的地理坐标 |
event.originalEvent | 浏览器事件 | 原始事件,可取 clientX 等 |
event.map | — | 事件所属地图实例 |
常用事件有 singleclick、dblclick、
pointermove、movestart、moveend。
点击请使用 singleclick,它已经过滤掉双击与拖动产生的伪点击。
点到哪个要素
map.forEachFeatureAtPixel 按像素位置查找要素,
可以限定图层与容差。容差让细线的点击更容易命中。
map.on('singleclick', function (event) {
var hit = map.forEachFeatureAtPixel(
event.pixel,
function (feature, layer) {
return { feature: feature, layer: layer };
},
{
hitTolerance: 6,
layerFilter: function (layer) {
return layer.get('queryable') !== false;
}
}
);
if (!hit) {
console.log('空白处', event.coordinate);
return;
}
console.log('命中', hit.feature.get('name'), '图层', hit.layer.get('title'));
});
回调返回非空值即停止继续查找,因此返回对象比返回 true
更有用。需要列出所有叠加要素时,在回调里收集后返回 undefined,
或者改用 forEachFeatureAtPixel 的遍历语义。
容差是像素,不是米。hitTolerance: 6 表示指针周围 6 像素内都算命中,
与缩放级别无关。放大后同样的数值覆盖的地面范围会变小。
像素与地理坐标互转
拖拽、定位标注、判断落点是否在范围内,都用到这两个方法。 它们都基于当前视图状态,视图改变后结果随之变化。
// 像素 -> 视图坐标
var coord = map.getCoordinateFromPixel([120, 80]);
// 视图坐标 -> 像素
var pixel = map.getPixelFromCoordinate(coord);
// 视图坐标 -> 经纬度(默认投影为 3857 时)
var lngLat = ol.proj.toLonLat(coord);
// 经纬度 -> 视图坐标
var viewCoord = ol.proj.fromLonLat([116.397, 39.908]);
console.log(coord, pixel, lngLat, viewCoord);
这些换算不依赖事件,任何时刻都能调用。但不要在 movestart
时读取,此时视图尚未完成更新,拿到的是上一帧的位置。
指针移动要节流
pointermove 每秒触发几十次,每次做要素查找会明显拖慢地图。
只处理变化幅度足够大的移动,并在没有命中时立刻恢复默认光标。
var lastPixel = null;
var ticking = false;
map.on('pointermove', function (event) {
if (lastPixel &&
Math.abs(event.pixel[0] - lastPixel[0]) + Math.abs(event.pixel[1] - lastPixel[1]) < 5) {
return;
}
lastPixel = event.pixel;
if (ticking) return;
ticking = true;
requestAnimationFrame(function () {
var hit = map.hasFeatureAtPixel(event.pixel, { hitTolerance: 4 });
map.getTargetElement().style.cursor = hit ? 'pointer' : '';
ticking = false;
});
});
hasFeatureAtPixel 只判断是否存在,找到第一个就返回,
比 forEachFeatureAtPixel 更适合悬停判断。
注意事项
click是底层浏览器事件,会与拖动冲突;地图级点击统一用singleclick。event.coordinate是视图投影坐标,不是经纬度,展示前要经过ol.proj.toLonLat。- 隐藏图层默认仍参与
forEachFeatureAtPixel,用layerFilter排除。 - 要素数量大时拾取会逐层遍历,配合
layerFilter只查必要图层能显著提速。 - 地图销毁前用
ol.Observable.unByKey解绑监听,否则回调仍持有地图引用。
经纬志(2026)。《地图事件与坐标换算》。https://www.jingweizhi.com/openlayers/tutorial/map-events.html(访问日期 2026-11-03)。