本节摘要:地图不只是看,还要操作。本节讲客户端绘制、量算、空间查询、要素交互——让地图"活"起来。
阅读完本节,你应当能够:
浏览器里能做轻量 GIS 分析:绘制点线面、量算距离面积、空间查询(点选、框选)、要素属性查看。复杂分析仍要后端,但客户端够用日常交互。
// OpenLayers 绘制 import Draw from 'ol/interaction/Draw'; const draw = new Draw({ source: vectorSource, type: 'Polygon' }); map.addInteraction(draw); draw.on('drawend', (e) => { const geom = e.feature.getGeometry(); const area = ol.sphere.getArea(geom); console.log('面积', area); });
绘制后可继续编辑顶点、移动要素,前端做轻量编辑。

| 量算 | 方法 |
|---|---|
| 距离 | 两点大地线距离(球面) |
| 面积 | 球面多边形面积 |
| 坐标 | 鼠标位置经纬度 |
量算要考虑球面(不是平面),用大地测量公式,否则误差大。
// 点选要素 map.on('click', (e) => { map.forEachFeatureAtPixel(e.pixel, (feature) => { showPopup(feature.getProperties()); }); });
客户端可做点选、框选、缓冲区查询,基于已加载的要素。大数据或复杂拓扑分析要发后端(PostGIS)。
Turf.js 是客户端空间分析库:
import * as turf from '@turf/turf'; const buffer = turf.buffer(point, 1, { units: 'kilometers' }); const intersect = turf.intersect(poly1, poly2);
支持缓冲、相交、量算、空间关系判断等,适合轻量分析。
| 维度 | 客户端 | 后端 |
|---|---|---|
| 数据量 | 小(已加载) | 大(数据库) |
| 复杂度 | 轻量 | 复杂 |
| 性能 | 受浏览器限制 | 服务器强 |
| 适合 | 交互、轻量 | 大数据、复杂 |
⚠️ 常见坑:客户端做大数据分析——浏览器内存和性能有限。大数据或复杂分析发后端 PostGIS,客户端只做轻量交互。
💡 关键直觉:客户端做绘制/量算/点选/框选等轻量交互,Turf.js 做空间分析。大数据和复杂拓扑发后端 PostGIS。
绘制交互最容易出错的是"状态管理":用户画到一半取消、重复进入绘制模式、绘制后临时图层残留。规范做法是维护一个绘制会话对象,统一管理开始、结束、清理:
import Draw from 'ol/interaction/Draw'; import Snap from 'ol/interaction/Snap'; let draw; // 当前绘制会话 function startDraw(map, source, type) { // 先清掉旧的绘制交互,避免叠加 map.removeInteraction(draw); draw = new Draw({ source, type }); map.addInteraction(draw); map.addInteraction(new Snap({ source })); // 捕捉到已有要素,画得更准 return draw; } function stopDraw(map) { map.removeInteraction(draw); }
绘制结束后的数据要能回传后端保存,一般把几何转成 GeoJSON 提交接口;同时前端要处理"取消绘制""绘制中按 ESC 键"等边界情况。
客户端点选看起来简单,完整链路涉及前端拿坐标、请求服务、解析结果、展示:
map.on('click', async (e) => { const coord = e.coordinate; // 视图坐标系 // 转成 WGS84 经纬度 const lonLat = ol.proj.toLonLat(coord); // 请求后端接口做空间查询(后端用 PostGIS) const resp = await fetch('/api/query-point?lon=' + lonLat[0] + '&lat=' + lonLat[1]); const features = await resp.json(); // 把结果加到图层展示 resultSource.clear(); resultSource.addFeatures(features.map(f => new Feature({ geometry: new GeoJSON().readGeometry(f.geometry) }))); });
关键细节是坐标系转换:前端视图可能是 3857,业务接口一般按 4326 经纬度传参,必须在客户端先 toLonLat,后端再 ST_Transform 或直接按 4326 查询,否则查询结果偏到别处。
客户端量算最容易被质疑的就是精度,几个细节要注意:
// Turf 球面距离示例 const from = turf.point([116.4, 39.9]); const to = turf.point([116.5, 39.95]); const km = turf.distance(from, to, { units: 'kilometers' });
一个实用的判断原则:单次操作涉及的数据量小于一万、计算不依赖数据库拓扑的,可以在客户端做;涉及全表、需要空间索引、要保证结果权威的,交给后端。这个边界会随硬件提升缓慢移动,但"交互走前端、权威走后端"的原则长期有效。出现"前端越写越重"的苗头时,回头把分析挪到服务端,往往比继续堆前端代码更省力。
第 4 章结束。下一章讲空间分析算法。