news 2026/7/30 20:22:36

OpenLayers 加载百度瓦片地图源(03)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenLayers 加载百度瓦片地图源(03)

OpenLayers加载百度瓦片地图详解

百度地图作为国内主流地图服务,其瓦片坐标系与标准Web墨卡托投影存在差异。本文将详细介绍如何在OpenLayers中正确加载百度地图瓦片,包括坐标转换原理、三种实现方案的对比,以及实际使用中的注意事项。

当前使用的OpenLayers版本

{"ol": "^10.9.0"}

一、百度地图瓦片特殊性分析

1.1 坐标系差异

百度地图使用BD09坐标系,是在GCJ02坐标系基础上进一步加密的坐标系。而OpenLayers默认使用EPSG:3857(Web墨卡托投影),两者的差异导致直接加载百度瓦片会出现位置偏移。

1.2 瓦片坐标规则

百度地图的瓦片坐标规则与标准XYZ方案存在以下差异:

特性标准XYZ百度瓦片
Y轴方向从上到下递增从下到上递增
起始编号0开始1开始(需要减1)
负数处理直接使用负数使用"M"前缀表示
坐标系EPSG:3857BD09(需转换)

1.3 核心转换公式

在OpenLayers中加载百度瓦片的关键转换逻辑:

// OpenLayers坐标 → 百度瓦片坐标letbaiduY=-openLayersY-1;letbaiduX=openLayersX<0?'M'+(-openLayersX):openLayersX;letfinalY=baiduY<0?'M'+(-baiduY):baiduY;

二、实现方案详解

2.1 XYZ Source方案(推荐)

createBDXYZSource是基于ol/source/XYZ的实现,也是目前推荐的方案:

importXYZfrom'ol/source/XYZ'importTileGridfrom'ol/tilegrid/TileGrid'import{getasolProjGet}from'ol/proj'exportfunctioncreateBDXYZSource(options){if(!options.url){thrownewError('Tile: no url provided')}// 生成百度地图的分辨率数组(0-18级)constresolutions=[]for(leti=0;i<=19;i++){resolutions[i]=Math.pow(2,18-i)}// 创建自定义瓦片网格consttilegrid=newTileGrid({origin:[0,0],resolutions:resolutions})const{url:tileUrl,...params}=optionsreturnnewXYZ({...params,projection:olProjGet('EPSG:3857'),tileGrid:tilegrid,// 核心:自定义瓦片URL生成函数tileUrlFunction:(tileCoord,pixelRatio,projection)=>{if(!tileCoord)return'';letz=tileCoord[0]letx=tileCoord[1]lety=tileCoord[2]// 坐标转换:OpenLayers的y(从上往下递增)// 转换为百度的y(从下往上递增,且起始编号偏移1)y=-y-1// 处理负数(百度要求用 'M' 前缀)if(x<0)x='M'+(-x)if(y<0)y='M'+(-y)returnoptions.url.replace('{x}',x).replace('{y}',y).replace('{z}',z)}})}

使用示例:

importMapfrom'ol/Map'importViewfrom'ol/View'importTileLayerfrom'ol/layer/Tile'import{createBDXYZSource}from'./BaiduSource'constmap=newMap({target:'map',layers:[newTileLayer({source:createBDXYZSource({url:'http://online3.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=pl&udt=20151021',crossOrigin:'anonymous'})})],view:newView({center:[116.404,39.915],// 北京市中心zoom:10,projection:'EPSG:3857'})})

2.2 ImageTile Source方案(新版本)

createBDImageTileSource是基于ol/source/ImageTile的实现,这是OpenLayers新版本推荐的替代方案:

importImageTilefrom'ol/source/ImageTile'importTileGridfrom'ol/tilegrid/TileGrid'import{getasolProjGet}from'ol/proj'exportfunctioncreateBDImageTileSource(options){if(!options.url){thrownewError('Tile: no url provided')}// 生成分辨率数组constresolutions=[]for(leti=0;i<=19;i++){resolutions[i]=Math.pow(2,18-i)}// 创建瓦片网格consttilegrid=newTileGrid({origin:[0,0],resolutions:resolutions})returnnewImageTile({...options,projection:olProjGet('EPSG:3857'),tileGrid:tilegrid,// URL生成函数:参数为(z, x, y)url:function(z,x,y){// 转换为百度所需的 y(取反并减 1)letbaiduY=-y-1// 处理负数(百度要求 M 前缀)letbaiduX=x<0?'M'+(-x):xletfinalY=baiduY<0?'M'+(-baiduY):baiduYreturnoptions.url.replace('{x}',baiduX).replace('{y}',finalY).replace('{z}',z)}})}

使用示例:

constbaiduLayer=newTileLayer({source:createBDImageTileSource({url:'http://online3.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=pl',crossOrigin:'anonymous'})})

2.3 TileImage Source方案(已弃用)

createBDTileImageSource是基于ol/source/TileImage的实现,该方案已被标记为弃用:

importTileImagefrom'ol/source/TileImage'importTileGridfrom'ol/tilegrid/TileGrid'import{getasolProjGet}from'ol/proj'exportfunctioncreateBDTileImageSource(options){if(!options.url){thrownewError('Tile: no url provided')}letresolutions=[]for(leti=0;i<=19;i++){resolutions[i]=Math.pow(2,18-i)}lettilegrid=newTileGrid({origin:[0,0],resolutions:resolutions})returnnewTileImage({crossOrigin:'anonymous',projection:olProjGet('EPSG:3857'),tileGrid:tilegrid,tileUrlFunction:(tileCoord,pixelRatio,projection)=>{if(!tileCoord)return''letz=tileCoord[0]letx=tileCoord[1]lety=-tileCoord[2]-1if(x<0)x='M'+-xif(y<0)y='M'+-yreturnoptions.url.replace('{x}',x).replace('{y}',y).replace('{z}',z)}})}

注意TileImage在OpenLayers新版本中已被弃用,建议迁移到XYZImageTile方案。

三、方案对比与选择建议

3.1 三种方案对比

方案数据源类型兼容性推荐度适用场景
XYZ Sourceol/source/XYZOpenLayers 6.x+⭐⭐⭐⭐⭐新项目首选,API稳定
ImageTile Sourceol/source/ImageTileOpenLayers 7.x+⭐⭐⭐⭐新版本迁移,官方推荐
TileImage Sourceol/source/TileImage已弃用⭐⭐旧项目维护,不推荐新使用

3.2 选择建议

  1. 新项目开发:优先使用createBDXYZSource,API简洁且稳定
  2. 升级OpenLayers版本:可以考虑迁移到createBDImageTileSource
  3. 维护旧项目:如果当前使用TileImage且运行正常,可以暂时保留,但建议规划迁移

四、百度地图瓦片URL配置

4.1 常用瓦片服务地址

百度地图提供多种样式瓦片服务:

// 标准地图瓦片constnormalUrl='http://online{s}.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=pl'// 卫星地图瓦片constsatelliteUrl='http://shangetu{s}.map.bdimg.com/it/u=x={x};y={y};z={z};v=009;type=sate&fm=46'// 路网图层瓦片constroadUrl='http://online{s}.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=sl'// 深色主题瓦片constdarkUrl='http://api{s}.map.bdimg.com/customimage/tile?&x={x}&y={y}&z={z}&udt=20170428&scale=1&ak=YOUR_AK&customid=dark'

4.2 缩放级别说明

百度地图支持0-18级缩放,对应的分辨率:

缩放级别分辨率(像素/米)说明
0262144全球视图
58192国家级
10256城市级
158街道级
181建筑级

五、常见问题与解决方案

5.1 跨域问题

问题描述:控制台报错Access to image at 'http://...' from origin '...' has been blocked by CORS policy

解决方案

// 在创建Source时设置crossOrigincreateBDXYZSource({url:'...',crossOrigin:'anonymous'// 关键配置})

5.2 瓦片加载不显示

可能原因

  1. 坐标转换逻辑错误
  2. 瓦片URL模板格式不正确
  3. 缩放级别超出百度地图支持范围

排查步骤

// 1. 检查地图中心点坐标(应为EPSG:3857格式)console.log(map.getView().getCenter())// 2. 检查当前缩放级别console.log(map.getView().getZoom())// 3. 打印生成的瓦片URLconsole.log('生成的瓦片URL:',tileUrl)

5.3 瓦片偏移问题

问题描述:地图加载正常,但位置有明显偏移

解决方案:确保使用正确的坐标转换公式:

// 正确的转换公式letbaiduY=-y-1// 不是 -y 或 -y + 1

六、总结

6.1 核心要点回顾

  1. 坐标转换是关键:百度地图使用独特的瓦片坐标系,必须进行正确的坐标转换
  2. 推荐使用XYZ Source:API稳定,兼容性好,是新项目的首选方案
  3. 注意跨域设置:设置crossOrigin: 'anonymous'解决跨域加载问题
  4. 理解瓦片URL结构:掌握百度地图瓦片服务的URL模板格式

6.2 代码仓库

本文示例代码已封装为可复用的工具函数,完整实现可参考项目中的BaiduSource.js文件,支持三种不同的数据源创建方式,满足不同项目需求。

完整代码:

// 官方推荐的替代方案是使用 ImageTile 数据源(新版本)importImageTilefrom'ol/source/ImageTile'// TileImage 数据源已被标记为弃用。(旧版本)importTileImagefrom'ol/source/TileImage'importXYZfrom'ol/source/XYZ'importTileGridfrom'ol/tilegrid/TileGrid'import{getasolProjGet}from'ol/proj'importdebugfrom'../debug/index.js'/** * 使用 XYZ 创建百度地图瓦片数据源(推荐版本) * * 百度地图瓦片采用自定义坐标系,需要将 OpenLayers 的 EPSG:3857 坐标 * 转换为百度自有的瓦片坐标(y 轴取反并 -1,负数用 'M' 前缀)。 * * @param {Object} options - 配置参数 * @param {string} options.url - 瓦片 URL 模板,必须包含 {x} {y} {z} 占位符 * 示例: "http://online{s}.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=pl" * @param {string} [options.crossOrigin] - 跨域策略,设置为 'anonymous' 可解决跨域加载问题 * @param {number} [options.minZoom=0] - 最小缩放级别 * @param {number} [options.maxZoom=18] - 最大缩放级别 * @param {number} [options.gutter] - 瓦片边缘像素扩展 * @param {boolean} [options.reprojectionErrorThreshold=0.5] - 重投影误差阈值 * @returns {XYZ} 百度地图 XYZ 瓦片数据源 * @example * createBDXYZSource({ * url: 'http://online{s}.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=pl&udt=20151021', * crossOrigin: 'anonymous' * }) */exportfunctioncreateBDXYZSource(options){if(!options.url){debug.error('Tile: no url provided')}constresolutions=[]for(leti=0;i<=19;i++){resolutions[i]=Math.pow(2,18-i)}consttilegrid=newTileGrid({origin:[0,0],resolutions:resolutions})const{url:tileUrl,...params}=optionsreturnnewXYZ({...params,projection:olProjGet('EPSG:3857'),tileGrid:tilegrid,// 核心:自定义瓦片URL生成函数 (url参数不要放进来,否则不会触发:tileUrlFunction函数)tileUrlFunction:(tileCoord,pixelRatio,projection)=>{if(!tileCoord)return'';letz=tileCoord[0];letx=tileCoord[1];lety=tileCoord[2];// OpenLayers 的 y(从上往下递增)// 转换为百度的 y(从下往上递增,且起始编号偏移 1)y=-y-1;// 处理负数(百度要求用 'M' 前缀)if(x<0)x='M'+(-x);if(y<0)y='M'+(-y);returnoptions.url.replace('{x}',x).replace('{y}',y).replace('{z}',z)}})}/** * 使用ImageTile创建百度地图瓦片数据源(新版本) * @param {Object} options - 配置参数,需包含 url 字段 * @returns {ImageTile} */exportfunctioncreateBDImageTileSource(options){if(!options.url){debug.error('Tile: no url provided')}constresolutions=[]for(leti=0;i<=19;i++){resolutions[i]=Math.pow(2,18-i)}consttilegrid=newTileGrid({origin:[0,0],resolutions:resolutions})returnnewImageTile({// crossOrigin: 'anonymous',...options,projection:olProjGet('EPSG:3857'),tileGrid:tilegrid,url:function(z,x,y){// 转换为百度所需的 y(取反并减 1)letbaiduY=-y-1;// 处理负数(百度要求 M 前缀)letbaiduX=x<0?'M'+(-x):x;letfinalY=baiduY<0?'M'+(-baiduY):baiduY;returnoptions.url.replace('{x}',baiduX).replace('{y}',finalY).replace('{z}',z)}})}/** * 使用TileImage创建百度地图瓦片数据源(旧版本)(已弃用) * @param {Object} options - 配置参数,需包含 url 字段 * @returns {ImageTile} */exportfunctioncreateBDTileImageSource(options){if(!options.url){debug.error('Tile: no url provided')}letresolutions=[]for(leti=0;i<=19;i++){resolutions[i]=Math.pow(2,18-i)}lettilegrid=newTileGrid({origin:[0,0],resolutions:resolutions})returnnewTileImage({crossOrigin:'anonymous',projection:olProjGet('EPSG:3857'),tileGrid:tilegrid,// 核心:自定义瓦片URL生成函数tileUrlFunction:(tileCoord,pixelRatio,projection)=>{if(!tileCoord)return''letz=tileCoord[0]letx=tileCoord[1]lety=-tileCoord[2]-1if(x<0)x='M'+-xif(y<0)y='M'+-yreturnoptions.url.replace('{x}',x).replace('{y}',y).replace('{z}',z)}})}

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/30 20:21:22

python核心语法-数据存储与运算

字面量与变量 字面量 含义 字面量&#xff1a;指程序中&#xff0c;直接书写的固定值(数据)&#xff0c;就称为字面量 类型及书写格式 数字类型 整数(int) &#xff1a;10 18 -5 0 浮点数/小数(float)&#xff1a;8.5 3.14 1.0 -3.5 布尔 (bool) [True False] 表达现实生活…

作者头像 李华
网站建设 2026/7/30 20:20:10

google-font-download实战案例:如何批量下载并配置多字体样式

google-font-download实战案例&#xff1a;如何批量下载并配置多字体样式 【免费下载链接】google-font-download Locally host Googles web fonts 项目地址: https://gitcode.com/gh_mirrors/go/google-font-download google-font-download是一款实用的命令行工具&…

作者头像 李华
网站建设 2026/7/30 20:17:54

5种企业级多语言部署策略:Baserow全球化架构深度解析

5种企业级多语言部署策略&#xff1a;Baserow全球化架构深度解析 【免费下载链接】baserow Build databases, automations, apps & agents with AI — no code. Open source platform available on cloud and self-hosted. GDPR, HIPAA, SOC 2 compliant. Best Airtable al…

作者头像 李华
网站建设 2026/7/30 20:14:36

C++日期格式化实战:实现稳定中文星期几输出的完整方案

1. 项目概述&#xff1a;为什么我们需要一个“带星期几”的日期格式化工具&#xff1f;在日常的C开发中&#xff0c;处理日期和时间是绕不开的活儿。无论是写日志系统、做数据分析&#xff0c;还是开发一个简单的待办事项应用&#xff0c;你总会遇到需要把日期以一种更友好、更…

作者头像 李华
网站建设 2026/7/30 20:12:24

单片机毕业设计-基于 STM32 的 OLED 显示密码锁安防装置设计 基于矩阵键盘的电子密码开锁装置设计与实现(012501)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机&#xff0c;Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华