- 前端
- GIS
- 数据可视化
【免费下载链接】openlayers
OpenLayers
OpenLayers 10.3.0 是一次以"新能力 + 破坏性升级"并重的重要版本:它引入了全新的WebGLVectorLayer(用于大规模矢量数据渲染)、内置 UTM 坐标变换支持、可直接对接欧洲航天局(ESA)哨兵影像服务的SentinelHub数据源,并为GeoTIFF数据源补齐了模型变换(旋转/倾斜/翻转瓦片网格)支持。同时,本版本包含两个需要升级注意的破坏性变更:transform()对未知投影不再静默降级,以及WebGLPointsLayer样式变量的声明位置调整。阅读完本文,你将掌握 10.3.0 全部新增 API 的用法、两个破坏性变更的迁移方案,以及背后的源码实现原理。
升级须知:两个破坏性变更
transform()函数对未知投影改为抛错
在 10.3.0 之前,src/ol/proj.js 中的transform()函数在遇到未注册的源投影或目标投影时,会静默应用"恒等变换"(identity transform),即把坐标原样返回。这很容易掩盖投影配置错误:例如把 EPSG:4326 的经纬度坐标交给一个拼错编码的投影,结果却是坐标"纹丝不动",导致地图位置完全错误且难以排查。
从 10.3.0 起,该函数在无法完成变换时会直接抛出Error。其实现位于 src/ol/proj.js#L620-L629:
export function transform(coordinate, source, destination) { const transformFunc = getTransform(source, destination); if (!transformFunc) { const sourceCode = get(source)?.getCode() ?? String(source); const destinationCode = get(destination)?.getCode() ?? String(destination); throw new Error( `No transform available between ${sourceCode} and ${destinationCode}`, ); } return transformFunc(coordinate, undefined, coordinate.length); }迁移建议:在调用transform()之前,先通过ol/proj模块的get()函数检查投影是否已注册——get()在找不到给定标识符对应的投影定义时会返回null:
import {get, transform} from 'ol/proj'; if (get('EPSG:32650') && get('EPSG:4326')) { const coord = transform([500000, 4649776], 'EPSG:32650', 'EPSG:4326'); }这一变更同样会影响transformExtent()、Geometry#transform()等所有底层依赖该函数的 API,请确保应用中涉及的所有投影编码都已通过proj4注册或内置支持(内置支持范围见下文 UTM 章节)。
WebGLPointsLayer样式格式变更:变量移至 options 根级
此前WebGLPointsLayer的样式变量(variables)被放在style对象内部,与filter混在一起:
// Before new WebGLPointsLayer({ style: { // variables were part of the `style` object variables: { minYear: 1850, maxYear: 2015, }, filter: ['between', ['get', 'year'], ['var', 'minYear'], ['var', 'maxYear']], }, source: vectorSource, })从 10.3.0 起,变量必须作为独立的顶层对象传入options:
// Now new WebGLPointsLayer({ style: { filter: ['between', ['get', 'year'], ['var', 'minYear'], ['var', 'maxYear']], }, variables: { minYear: 1850, maxYear: 2015, }, source: vectorSource, })值得注意的是,WebGLPointsLayer本身并不属于稳定 API(在 changelog/upgrade-notes.md 中也有明确说明),并且该图层在后续 10.4.0 版本中已被弃用,官方推荐使用本文接下来介绍的WebGLVectorLayer替代。如果你仍在使用WebGLPointsLayer,请按上述新格式迁移,并为迁移到WebGLVectorLayer做好准备。
新特性:WebGLVectorLayer图层
WebGLVectorLayer(源码位于 src/ol/layer/WebGLVector.js)是 10.3.0 引入的全新图层类,专门针对大规模矢量数据集的渲染进行优化,它使用 WebGL 渲染管线,比传统的 Canvas 矢量渲染拥有更高的吞吐能力。官方在其类注释中描述为 "Layer optimized for rendering large vector datasets"。
核心 API
该图层直接继承Layer,其Options与WebGLPointsLayer类似,关键选项包括:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
source | VectorSource | - | 矢量数据源 |
style | FlatStyleLike | - | 扁平化样式(flat style),支持filter条件、['var', ...]、['get', ...]等表达式 |
variables | StyleVariables | {} | 样式变量,每个变量必须是字面值(非表达式),可在样式属性中通过['var', 'varName']引用 |
opacity | number | 1 | 图层不透明度 |
disableHitDetection | boolean | false | 设为true可带来轻微性能提升,但会禁用该图层上的所有命中检测 |
background | BackgroundColor | - | 图层背景色 |
minZoom/maxZoom等 | number | - | 可见性控制 |
需要注意:WebGLVector图层在移除时必须手动调用dispose(),否则底层 WebGL 上下文不会被垃圾回收,造成资源泄漏。
从 src/ol/layer/WebGLVector.js#L79 的源码可以看到,构造时传入的variables被保存为私有属性this.styleVariables_,并提供了updateStyleVariables()方法用于运行时更新变量。
实战示例
examples/webgl-vector-layer.js 展示了完整的用法:使用多个扁平样式规则实现"高亮 + 默认 + 文本标注"三层样式,并通过updateStyleVariables()在鼠标移动时动态更新高亮变量:
const style = [ { filter: ['==', ['var', 'highlightedId'], ['id']], style: { 'stroke-color': 'white', 'stroke-width': 3, 'stroke-offset': -1, 'fill-color': [255, 255, 255, 0.4], }, }, { else: true, style: { 'stroke-color': ['*', ['get', 'COLOR'], [220, 220, 220]], 'stroke-width': 2, 'stroke-offset': -1, 'fill-color': ['*', ['get', 'COLOR'], [255, 255, 255, 0.6]], }, }, { style: { 'text-value': ['coalesce', ['get', 'ECO_NAME'], 'unknown'], 'text-font': 'bold 12px "Open Sans", "Arial Unicode MS", sans-serif', 'text-fill-color': 'rgb(0,0,0)', 'text-stroke-color': 'rgba(255, 255, 255, 0.8)', 'text-stroke-width': 2, 'text-overflow': false, }, }, ]; const vectorLayer = new WebGLVectorLayer({ source: new VectorSource({ url: 'https://openlayers.org/data/vector/ecoregions.json', format: new GeoJSON(), }), style, variables: { highlightedId: -1, }, }); // 运行时更新变量 vectorLayer.updateStyleVariables({highlightedId: id});样式规则数组中的多条规则按顺序求值,filter命中即采用对应样式;['var', 'highlightedId']与updateStyleVariables()的组合是 WebGL 系列图层实现交互高亮的标准模式。对应示例 examples/webgl-vector-layer.html 可直接运行查看效果。
新特性:SentinelHub数据源
SentinelHub(源码位于 src/ol/source/SentinelHub.js)是一个基于Sentinel Hub Processing API的瓦片数据源,它继承自DataTileSource,允许开发者用 JavaScript 编写 Evalscript,直接对欧空局哨兵(Sentinel)卫星影像执行波段运算并渲染为地图瓦片。
工作原理
从源码结构看,其工作流是"三步就绪"模式:构造时auth(认证)、data(输入数据)、evalscript(处理脚本)三者缺一不可,任一项缺失时数据源保持loading状态,不会渲染;三者齐备后由fireWhenReady_()将状态置为ready并触发渲染(src/ol/source/SentinelHub.js#L589-L600)。因此三个核心配置均提供了对应的 setter 方法以便延迟配置:
setAuth(auth):接受{clientId, clientSecret}对象(此时会请求 OAuth2 token)或直接传入 access token 字符串;token 过期前 60 秒会自动刷新(src/ol/source/SentinelHub.js#L500-L533);setData(data):设置输入数据配置;setEvalscript(evalscript):接受字符串形式的 Evalscript,或包含setup/evaluatePixel/updateOutput等函数的对象——对象形式会被serializeEvalscript()序列化成以//VERSION=3开头的脚本字符串提交给服务端执行;setFormat(format):设置响应 MIME 类型。
瓦片加载时,源码通过loadTile_()向 Processing API 发起POST请求,请求体包含input.bounds.bbox(当前瓦片范围)、input.data(数据配置)、output(瓦片尺寸与响应格式)以及evalscript(src/ol/source/SentinelHub.js#L619-L684)。请求头使用Authorization: Bearer <token>。当遭遇 HTTP 429(限流)时,源码会忽略Retry-After头,改用指数退避策略重试(最多 10 次,基础延迟 500ms,即500ms × 2^attempt)。
核心配置参数
构造函数的Options完整定义见 src/ol/source/SentinelHub.js#L377-L398,要点如下:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
auth | AuthConfig \| string | - | {clientId, clientSecret}或直接给 token;未提供时需后续调用setAuth() |
data | Array<ProcessRequestInputDataItem> | - | 输入数据配置,如{type: 'sentinel-2-l2a', dataFilter: {...}} |
evalscript | Evalscript \| string | - | 处理脚本,函数对象会被序列化为 Evalscript |
tileSize | number \| Size | [512, 512] | 瓦片像素宽高 |
url | string | https://services.sentinel-hub.com/api/v1/process | Processing API 地址 |
format | string | image/png | 响应 MIME 类型,仅支持image/png、image/jpeg、image/webp(源码中通过knownImageMediaTypes白名单校验,非法值会在构造时抛错) |
projection | ProjectionLike | 视图投影 | 瓦片投影 |
wrapX | boolean | true | 是否跨反经线(东西方向环绕)渲染 |
interpolate | boolean | true | 重采样时是否使用线性插值,设为false使用最近邻 |
数据项的dataFilter支持timeRange({from, to}时间范围)和maxCloudCoverage(0-100 的最大云量)过滤;type可指定如sentinel-2-l2a等集合。坐标参考系通过getProjectionIdentifier()转换为 Sentinel Hub 接受的 OGC CRS URI 形式(如http://www.opengis.net/def/crs/EPSG/0/4326,src/ol/source/SentinelHub.js#L329-L351)。
实战示例
examples/sentinel-hub.js 演示了完整的接入流程——构造时只配置data与evalscript,认证凭据由用户通过表单提交后调用setAuth()注入:
useGeographic(); const source = new SentinelHub({ data: [ { type: 'sentinel-2-l2a', dataFilter: { timeRange: { from: '2024-05-30T00:00:00Z', to: '2024-06-01T00:00:00Z', }, }, }, ], evalscript: { setup: () => ({ input: ['B12', 'B08', 'B04'], output: {bands: 3}, }), evaluatePixel: (sample) => [ 2.5 * sample.B12, 2 * sample.B08, 2 * sample.B04, ], }, }); // 用户提交 clientId / clientSecret 后激活数据源 source.setAuth({clientId, clientSecret}); // 配置错误时通过 change 事件 + getError() 获取详细信息 source.on('change', () => { if (source.getState() === 'error') { alert(source.getError()); } });上面的 Evalscript 使用 B12/B08/B04 三个波段做加权合成,模拟近红外假彩色影像。当数据源配置或认证失败时,源码会将状态置为error并触发change事件,通过getError()(src/ol/source/SentinelHub.js#L700-L702)可以拿到具体错误对象用于调试。仓库中还提供了 examples/sentinel-hub-custom-format.js(自定义image/jpeg格式)与 examples/sentinel-hub-custom-script.js(自定义脚本)两个变体示例。
ImageTile数据源的多项增强
10.3.0 对近期引入的ImageTile数据源(源码位于 src/ol/source/ImageTile.js)做了多项增强。ImageTileSource继承自DataTileSource,用于加载图片类型的瓦片,其核心特点是 URL 模板、数组或函数三种方式定义瓦片地址:
// 单一 URL 模板 new ImageTileSource({ url: 'https://example.com/tiles/{z}/{x}/{y}.png', }); // URL 数组(自动轮换负载均衡) new ImageTileSource({ url: ['https://a.example.com/{z}/{x}/{y}.png', 'https://b.example.com/{z}/{x}/{y}.png'], }); // 函数形式,完全自定义 URL 逻辑 new ImageTileSource({ url: (z, x, y) => `https://example.com/tiles/${z}/${x}/${y}.png`, });源码内部会把字符串/数组形式的 URL 通过makeLoaderFromTemplates()与renderXYZTemplate()展开为加载函数(src/ol/source/ImageTile.js#L63-L96);也支持直接提供自定义loader函数。在 10.3.0 中新增的关键增强包括:
zDirection选项:控制当视图分辨率处于两个整数缩放级别之间时,选择更高还是更低的瓦片级别,可传数字或NearestDirectionFunction(src/ol/source/ImageTile.js#L48-L51);{-y}占位符修复:修复了{-y}(翻转 Y 坐标)占位符在ImageTile源中的处理错误,使其与XYZ等源行为一致;TileDebug改为ImageTile的子类:调试瓦片网格图层从此前的XYZ基类迁移到ImageTile基类,便于与新的图片瓦片体系配合调试;- 属性/选项补充:
transition(新瓦片淡入时长)、interpolate、crossOrigin、referrerPolicy等均可在构造时配置。
仓库中的 examples/pmtiles-image.js 演示了如何从 PMTiles 归档读取图片瓦片并喂给ImageTile源,展示了loader自定义加载的典型场景。
GeoTIFF数据源的模型变换支持
10.3.0 为GeoTIFF数据源(源码位于 src/ol/source/GeoTIFF.js)新增了模型变换(model transformation)支持,使其能够正确处理旋转、倾斜或翻转(flipped)的瓦片网格。此前这类非标准网格的 GeoTIFF 影像(例如某些合成孔径雷达 SAR 产品)无法被正确配准到地图上。
在源码中,当 TIFF 的fileDirectory中带有ModelTransformation标签时,会读取其 4×4 仿射变换矩阵的前 8 个元素,并据此调整瓦片网格的配准(src/ol/source/GeoTIFF.js#L637-L642):
const modelTransformation = image.fileDirectory.getValue( // ... GeoTIFF ModelTransformation tag 读取 ); if (modelTransformation) { const [a, b, c, d, e, f, g, h] = modelTransformation; // 依据该矩阵构造旋转/倾斜/翻转感知的网格变换 }仓库中的 examples/cog-modeltransformation.js 展示了直接加载一张带模型变换的 Cloud Optimized GeoTIFF(来自 UMBRA 开放 SAR 数据目录)并叠加在 OSM 底图上的完整流程:通过cogSource.getView()获取源投影与范围,配合proj4注册自定义投影,再用transformExtent()将源范围变换到视图投影后fit到视口:
register(proj4); const cogSource = new GeoTIFF({ sources: [{url: 'https://umbra-open-data-catalog.s3.amazonaws.com/.../xxx_GEC.tif'}], }); // ... 创建地图并叠加底图 ... cogSource.getView().then((viewConfig) => fromProjectionCode(viewConfig.projection.getCode()).then(() => { const view = map.getView(); view.fit( transformExtent( viewConfig.extent, viewConfig.projection, view.getProjection(), ), ); }), );内置 UTM 坐标变换
10.3.0 在ol/proj中加入了开箱即用的 UTM 投影支持(实现位于 src/ol/proj/utm.js),无需额外注册 proj4 定义即可在 UTM 投影与经纬度之间互转。
支持的投影编码与判定逻辑
源码中的zoneFromCode()(src/ol/proj/utm.js#L210-L236)通过正则识别以下三种编码形式:
EPSG:326XX—— 北半球 UTM 带(如 EPSG:32650 为北半球 50 带);EPSG:327XX—— 南半球 UTM 带(如 EPSG:32750 为南半球 50 带);urn:ogc:def:crs:EPSG::XXXX与http://www.opengis.net/def/crs/EPSG/0/XXXX形式的等价 URI。
其中 32601–32660 判定为北半球,32701–32760 判定为南半球。makeProjection()与makeTransforms()(src/ol/proj/utm.js#L270-L292)分别负责创建投影实例(单位为米)与正反变换函数,fromLonLat()/toLonLat()内部实现了基于 WGS84 椭球(长半轴 6378137m)的标准横轴墨卡托算法,中央经线通过zoneToCentralLongitude()((zone - 1) * 6 - 180 + 3)计算,东偏移量为 500000m,南半球北偏移量附加 10000000m,比例因子K0 = 0.9996。
使用方式
import {transform} from 'ol/proj'; // 经纬度 → UTM 北半球 50 带 const utm = transform([116.39, 39.9], 'EPSG:4326', 'EPSG:32650'); // UTM → 经纬度 const lonLat = transform(utm, 'EPSG:32650', 'EPSG:4326');需要说明的是,源码注释明确指出这套算法提供的是近似变换:"The functions here provide approximate transforms to and from UTM. They are not appropriate for use beyond the validity extend of a UTM zone, and the accuracy of the transform decreases toward the zone edges." 即其精度在远离所在带中央经线时会下降,不适合超出带有效范围使用;对精度要求严苛的场景仍建议使用 proj4 注册完整定义。同时,fromLonLat()会将纬度钳制在 -80°~84° 之间,这与 UTM 系统的有效范围一致。
渲染与性能改进
常规形状与图标样式的智能缓存
10.3.0 将常规形状(RegularShape)样式接入IconImageCache(相关改动见 PR #16349,配套的 PR #16362 使用Math.ceil()计算常规形状的画布尺寸)。这意味着大量使用同一形状/图标样式的地图(如海量点标注)可以共享缓存图像,避免重复绘制,显著降低渲染开销与内存占用;同时使用Math.ceil()取整画布尺寸,规避了亚像素导致的渲染毛边。
VectorImageLayer的 TypeScript 泛型修复
PR #16348 修复了VectorImageLayer(源码位于 src/ol/layer/VectorImage.js)的类型泛型定义,使其能够正确推导出图层关联矢量数据源的特征类型。对 TypeScript 用户而言,new VectorImageLayer({source})后getSource()返回的特征类型不再退化为FeatureLike,IDE 提示与类型检查更加精准。
渲染事件与可见性语义
PostRenderFunction的FrameState永不为 null(PR #16415):此前在某些场景下 postrender 回调拿不到frameState,现在postrender函数始终能获得有效帧状态;配套改动(PR #16268、#16277)确保在没有帧状态时不调用 postrender 函数,并支持在 Worker 中执行 postrender 函数;isVisible()语义调整(PR #16260):尚未完成渲染的图层,isVisible()现在返回false,避免在首帧渲染前产生错误的可见性判断;- 使用
event.pixel替代getEventPixel(PR #16395):简化了事件坐标的获取路径。
文本标注与图层选项
keepUpright参数(PR #16302、#16315):新增text-keep-upright扁平样式属性,控制文本标签在旋转视图/旋转要素上是否保持直立,避免倒置文字影响可读性;- 恢复
setDeclutter()方法(PR #16383):ol/layer/Vector重新支持通过setDeclutter()动态开关标签去重(declutter); background进入Tile图层选项(PR #16371):瓦片图层现在可以直接配置背景色;overlaps选项的 setter(PR #16243):为相关图层/渲染器补充了overlaps的运行时设置方法;- URI 组件编码修复(PR #16409):修正了 URL/URI 组件未正确编码的问题,避免含特殊字符的请求地址失效。
其他值得关注的变化
- 投影与 OGC 服务:OGC TileMatrixSet 的
crs现在可以接受带 URI 字符串的 CRS 对象(PR #16291),并可从 TileMatrixSet 的 CRS 自动设置源投影(PR #16293); - 命中检测修正(PR #16393):
hitDetection只对点要素生效,避免对线/面产生误判; Modify交互 API 改进(PR #16296):程序化调用 Modify 交互的接口更完善;- 数据压缩库替换(PR #16254):用
fflate替换jszip,减小打包体积; RBush的forEach支持模板参数(PR #16345):类型层面增强;- 依赖升级:
proj4升级到 2.15.0、typescript升级到 5.7.2、jsts升级到 2.12.1、rollup升级到 4.27.4 等(完整列表见 changelog/v10.3.0.md 的 Dependency Updates 小节)。
迁移总结
升级到 10.3.0 时,需要重点检查两处:
- 投影变换调用:确认所有
transform()调用涉及的投影编码都已注册(get(code) !== null),否则会抛出No transform available between ...错误; WebGLPointsLayer样式:将style.variables迁移到 options 根级,并留意该图层 API 不稳定、官方后续推荐WebGLVectorLayer的事实。
对于新项目,建议直接采用WebGLVectorLayer+ 扁平样式 +updateStyleVariables()的方案处理大规模矢量数据,用SentinelHub数据源接入哨兵影像处理流程,并利用开箱即用的 UTM 变换简化投影处理代码。
- 前端
- GIS
- 数据可视化
【免费下载链接】openlayers
OpenLayers
相关推荐
提升深度学习模型性能:tinygrad图像数据增强完全指南
提升深度学习模型性能:tinygrad图像数据增强完全指南 tinygrad是一个轻量级的深度学习框架,它结合了PyTorch的易用性和JAX的函数式变换特性,
人工智能深度学习大模型Foundations-of-LLMs数据增强:文本生成与变换
Foundations of LLMs数据增强:文本生成与变换 引言:为什么数据增强在大语言模型中至关重要? 在大语言模型(Large Language Mod
文档教程大模型Presto 0.249 版本技术解读:Hive 3 元数据、数据交换校验与内存治理增强
Presto 0.249 版本技术解读:Hive 3 元数据、数据交换校验与内存治理增强 Presto 0.249 是一个聚焦稳定性的演进版本,重点解决了 Hi
大数据数据库后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考