尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Leaflet 中 WMS 图层的 CRS 配置:基于 EPSG:4326 的完整实战解析

Leaflet 中 WMS 图层的 CRS 配置:基于 EPSG:4326 的完整实战解析 Leaflet 中 WMS 图层的 CRS 配置基于 EPSG:4326 的完整实战解析【免费下载链接】Leaflet JavaScript library for mobile-friendly interactive maps 项目地址: https://gitcode.com/gh_mirrors/le/LeafletWMSWeb Map Service是 GIS 行业发布地图服务的标准协议而 WMS 请求中的每个图片都由其四角坐标bbox决定——这就绕不开坐标系CRS的选择。本篇文章以 Leaflet 仓库中的 wms-example-crs 示例 为核心讲解如何在 Leaflet 中为 WMS 图层显式指定EPSG:4326坐标系、其背后的源码实现原理bbox 计算、srs/crs 参数切换以及内置 CRS 家族与使用限制读完你即可在自己的项目中正确配置任意 Leaflet 内置坐标系下的 WMS 图层。示例文档定位WMS 教程中的 CRS 专项示例docs/examples/wms/wms-example-crs.md是 Leaflet 官方 WMS 教程docs/examples/wms/index.md中“面向 GIS 用户的注意事项Notes to GIS Users of WMS Services”一节的配套演示帧。它通过{% include frame.html urlwms-example-crs.html %}被嵌入教程正文演示的核心技术点是当 WMS 服务的输出坐标系不是默认的 Web 墨卡托EPSG:3857时如何通过在地图初始化时设置crs: EPSG4326让地图及其上的 WMS 图层切换到经纬度等距投影。该示例完整的源码只有一段 ES Module 脚本script typemodule import {LeafletMap, EPSG4326, WMSTileLayer} from leaflet; const map new LeafletMap(map, { center: [0, 0], zoom: 1, crs: EPSG4326 }); const wmsLayer new WMSTileLayer(http://ows.mundialis.de/services/service?, { layers: TOPO-OSM-WMS }).addTo(map); /script这段代码与教程中其他三个 WMS 示例wms-example1.md、wms-example2.md、wms-example3.md的唯一区别正是crs: EPSG4326这一行——其余示例均使用默认坐标系。理解这一行配置就理解了 Leaflet 中 WMS 与坐标系的关系。为什么 WMS 必须关心坐标系Leaflet 默认使用 EPSG:3857Web 墨卡托适合 OSM 等常规瓦片底图。但 WMS 服务由专业 GIS 软件发布其服务端在渲染地图图片时需要知道客户端请求的图片对应的地理范围。这个范围以投影坐标的形式通过bbox参数传给服务端而投影坐标的计算完全取决于坐标系。在 Leaflet 中WMSTileLayer并不是简单地把地图中心点发给服务端而是把每个瓦片四角的经纬度通过当前 CRS 的project()方法投影成坐标值再拼进请求 URL。这一点在 WMSTileLayer.js 的getTileUrl()中体现得淋漓尽致getTileUrl(coords) { const tileBounds this._tileCoordsToNwSe(coords), crs this._crs, bounds new Bounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])), min bounds.min, max bounds.max, bbox (this._wmsVersion 1.3 this._crs EPSG4326 ? [min.y, min.x, max.y, max.x] : [min.x, min.y, max.x, max.y]).join(,); const url new URL(super.getTileUrl(coords)); for (const [k, v] of Object.entries({...this.wmsParams, bbox})) { url.searchParams.append(this.options.uppercase ? k.toUpperCase() : k, v); } return url.toString(); }可见bbox 的数值单位取决于crs而 Leaflet 对 EPSG:4326 还做了参数顺序的特殊处理见后文。如果客户端与服务端坐标系不一致请求出来的图片就会错位或变形这正是本示例要求显式设置crs的原因。EPSG:4326 在 Leaflet 中的定义EPSG4326是 Leaflet 内置的坐标系之一定义在 src/geo/crs/EPSG4326.jsexport class EPSG4326 extends EarthCRS { static code EPSG:4326; static projection LonLat; static transformation new Transformation(1 / 180, 1, -1 / 180, 0.5); }它使用简单的等距圆柱投影LonLat即经纬度直接作为投影坐标因此坐标单位是度degree而不是米。文件头部的注释还特别说明了两条重要约定Leaflet 遵循 TMS 规范中 EPSG:4326 的全球地理网格zoom 0 时整个世界由两块 256×256 像素的瓦片覆盖瓦片坐标原点位于 (-180, 90)若开启tms选项则原点在 (-180, -90)。这意味着在EPSG:4326下地图的瓦片网格与默认墨卡托完全不同——使用该坐标系时不要混用为 EPSG:3857 设计的瓦片服务。Leaflet 内置的 CRS 全集可以从 src/geo/crs/index.js 一览export {CRS} from ./CRS.js; export {EarthCRS} from ./EarthCRS.js; export {EPSG3395} from ./EPSG3395.js; export {EPSG3857, EPSG900913} from ./EPSG3857.js; export {EPSG4326} from ./EPSG4326.js; export {SimpleCRS} from ./SimpleCRS.js;教程原文明确指出Leaflet 对 WMS 场景常用坐标系的官方支持范围是CRS:3857、CRS:3395 与 CRS:4326三种CRS 基类说明见 src/geo/crs/CRS.js。如果你的 WMS 服务使用其他坐标系如 UTM 分带投影则需要借助 Proj4Leaflet 这类第三方插件扩展——这不在内置能力之内。源码级解析WMSTileLayer 如何与 CRS 协同工作1. 默认 WMS 参数WMSTileLayer.js 通过defaultWmsParams定义了发送给 WMS 服务的标准参数与默认值参数默认值说明serviceWMS服务类型requestGetMap请求操作layers必填逗号分隔的 WMS 图层列表styles逗号分隔的 WMS 样式列表formatimage/jpeg图片格式需要透明背景时改用image/pngtransparentfalse是否请求带透明通道的图片version1.1.1WMS 服务协议版本此外还有两个图层级选项同文件 L58-L67crs: null——WMS 请求使用的坐标系默认跟随地图 CRS本示例即此情形uppercase: false——若为trueWMS 请求参数名将变为大写。2. 初始化未知选项透传给服务端initialize()L70-L89中有一个非常实用的设计凡是不属于 WMSTileLayer/TileLayer 已知选项的键都会被原样合并进wmsParams作为额外参数发送给 WMS 服务端。这为传递 Geoserver 等产品的厂商私有参数vendor parameters提供了通道。同时width与height会根据瓦片尺寸自动计算支持 Retina 时翻倍。3. 挂载到地图srs 与 crs 的版本切换在onAdd()L91-L100中图层先取options.crs ?? map.options.crs决定本次请求使用的坐标系然后依据 WMS 版本号选择投影参数的键名this._crs this.options.crs ?? map.options.crs; this._wmsVersion parseFloat(this.wmsParams.version); const projectionKey this._wmsVersion 1.3 ? crs : srs; this.wmsParams[projectionKey] this._crs.code;WMS1.3.0 及以上版本使用crs参数WMS1.1.1 及以下版本使用srs参数。参数值即 CRS 的code如EPSG:4326EPSG4326类中static code EPSG:4326正是为此定义。由于默认version为1.1.1本示例实际发出的请求会携带srsEPSG:4326。4. bbox 参数顺序的 EPSG:4326 特例回到getTileUrl()的 bbox 拼接逻辑bbox (this._wmsVersion 1.3 this._crs EPSG4326 ? [min.y, min.x, max.y, max.x] : [min.x, min.y, max.x, max.y]).join(,);常规情况下 bbox 顺序为minx, miny, maxx, maxyX 在前仅当WMS 版本 ≥ 1.3且坐标系是EPSG4326时才切换为miny, minx, maxy, maxx即纬度在前、经度在后的lat,lon顺序。这是因为 WMS 1.3.0 规范要求 bbox 的坐标轴顺序与 CRS 定义一致而 EPSG:4326 的官方轴顺序就是纬度在前。Leaflet 在源码中做了这个特判避免了服务端因坐标轴顺序不符而报错或返回错误区域。这是本示例选择EPSG4326时最容易被忽略、也最值得注意的底层细节。5. 动态更新参数setParamssetParams(params, noRedraw)L119-L130可以将新参数合并进wmsParams并默认重绘当前屏幕瓦片适合运行时切换样式、滤镜等场景wmsLayer.setParams({styles: population, transparent: true, format: image/png});完整可运行的示例与验证将 wms-example-crs 示例放到实际页面中运行需要leaflet的 ESM 构建产物当前仓库版本为2.0.0-alpha.1见 package.json!DOCTYPE html html head meta charsetutf-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / titleWMS CRS Example/title link relstylesheet hrefdist/leaflet.css / /head body div idmap stylewidth: 100%; height: 400px;/div script typemodule import {LeafletMap, EPSG4326, WMSTileLayer} from leaflet; const map new LeafletMap(map, { center: [0, 0], zoom: 1, crs: EPSG4326 }); const wmsLayer new WMSTileLayer(http://ows.mundialis.de/services/service?, { layers: TOPO-OSM-WMS }).addTo(map); /script /body /html运行后观察网络请求每个瓦片的 GetMap 请求应形如http://ows.mundialis.de/services/service?layersTOPO-OSM-WMSwidth256height256serviceWMSrequestGetMapstylesformatimage%2Fjpegtransparentfalseversion1.1.1srsEPSG%3A4326bbox...其中srsEPSG:4326与按minx, miny, maxx, maxy顺序组织、单位为度的 bbox正是本示例区别于默认墨卡托示例的关键证据。仓库中的单元测试 spec/suites/layer/tile/WMSTileLayerSpec.js 也验证了构造函数的行为describe(constructor, () { it(sets wmsParams, () { const layer new WMSTileLayer(https://example.com/map, {opacity: 0.5, attribution: foo}); expect(layer.wmsParams).to.eql({ ...layer.defaultWmsParams, width: 256, height: 256 }); }); });可见opacity、attribution等 Leaflet 已知选项不会进入 WMS 请求参数而width、height会被自动补全——这印证了“已知选项本地消费、未知选项透传服务端”的设计。扩展实践多个 WMS 图层与图层控件组合在真实项目中CRS 配置通常与多图层切换组合使用。参考教程示例 wms-example3.md 与图层控件示例源码见 src/control/LayersControl.js可以同时声明多个WMSTileLayer并通过LayersControl切换import {LeafletMap, EPSG4326, WMSTileLayer, LayersControl} from leaflet; const map new LeafletMap(map, { center: [0, 0], zoom: 1, crs: EPSG4326 }); const basemaps { Topography: new WMSTileLayer(http://ows.mundialis.de/services/service?, { layers: TOPO-WMS }), Places: new WMSTileLayer(http://ows.mundialis.de/services/service?, { layers: OSM-Overlay-WMS }), Topography, then places: new WMSTileLayer(http://ows.mundialis.de/services/service?, { layers: TOPO-WMS,OSM-Overlay-WMS }) }; new LayersControl(basemaps, {}, {collapsed: false}).addTo(map); basemaps.Topography.addTo(map);注意两点layers是逗号分隔的列表多图层写在一个WMSTileLayer里会由 WMS 服务端合成一张图片返回一次请求如果拆成多个WMSTileLayer叠加则是多次请求、由浏览器端合成。两者合成顺序与效果不同。所有图层都继承地图的EPSG4326坐标系除非在图层选项里显式覆盖crs因此只需在地图初始化时设置一次。使用限制与注意事项GIS 用户必读结合教程“Notes to GIS Users of WMS Services”一节的说明与源码使用本方案时有以下边界需要明确Leaflet 不解析GetCapabilities文档。你必须在构造WMSTileLayer时直接提供基础 URL不带任何参数例如http://ows.mundialis.de/services/service?并手动指定layers等参数。可用 QGIS 等桌面 GIS 软件查看 WMS 服务定义了哪些图层名。不支持图例legend与GetFeatureInfo。这些属于完整 WMS 客户端能力Leaflet 仅实现瓦片渲染部分。内置坐标系有限仅 EPSG:3857、EPSG:3395、EPSG:4326外加面向非地理场景的SimpleCRS。其他坐标系需要引入 Proj4Leaflet 插件扩展此时同样是在地图初始化时设置自定义crsWMS 图层的 bbox 计算逻辑crs.project依然适用。无文档记载的选项会被透传给服务端WMSTileLayer的完整选项以 API 参考文档 为准任何未列出的选项都会进入每个请求的 URL 参数。小结wms-example-crs.md虽然只有短短一段脚本却浓缩了 Leaflet 中 WMS 与坐标系协同工作的完整链路crs决定投影坐标的单位与瓦片网格、srs/crs参数随 WMS 版本切换、EPSG:4326 触发 bbox 轴序特判、未知选项透传服务端。理解这条链路后你不仅能让 EPSG:4326 的 WMS 服务正确显示也能举一反三为任何内置或插件提供的坐标系正确配置 WMS 图层。若想查看完整教程与其余示例可继续阅读 docs/examples/wms/index.md 及 wms-example1.md、wms-example2.md、wms-example3.md。【免费下载链接】Leaflet JavaScript library for mobile-friendly interactive maps 项目地址: https://gitcode.com/gh_mirrors/le/Leaflet创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表