与底图结合:瓦片、投影与偏移
deck.gl 不提供底图,只提供坐标系。要让数据落在地图上, 要么自己加载瓦片,要么把它挂到已有的地图库里。 两条路都绕不开 Web Mercator 和坐标偏移。
三种结合方式
| 方式 | 做法 | 适用 |
|---|---|---|
| 自带瓦片 | TileLayer + BitmapLayer 叠在数据图层下面 |
只需要静态底图,不想引入第二个地图库 |
| 交给地图库 | MapLibre / Mapbox 提供底图,deck.gl 用 MapboxOverlay 叠加 |
需要底图交互、注记、图层控制 |
| 与二维地图联动 | Leaflet / OpenLayers 管底图,deck.gl 作为同步画布覆盖其上 | 已有地图应用,增量引入 GPU 图层 |
三种方式的数据坐标都是 [经度, 纬度],区别在于谁负责把视角变化
通知给 deck.gl。选错方式会导致拖动底图时数据不跟随,这是集成阶段最常见的问题。
用 TileLayer 加载瓦片
TileLayer 是复合图层:它按当前视角计算需要的瓦片,
为每块瓦片生成一个子图层。子图层通常用 BitmapLayer,
通过 renderSubLayers 拿到瓦片图像和地理范围。
var basemap = new deck.TileLayer({
id: 'basemap',
data: 'https://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Base/MapServer/tile/{z}/{y}/{x}',
minZoom: 0,
maxZoom: 16,
tileSize: 256,
renderSubLayers: function (props) {
var bb = props.tile.boundingBox; // [[西, 南], [东, 北]]
return new deck.BitmapLayer(props, {
data: null,
image: props.data,
bounds: [bb[0][0], bb[0][1], bb[1][0], bb[1][1]]
});
}
});
var deckgl = new deck.Deck({
canvas: 'cv',
width: '100%',
height: '100%',
initialViewState: { longitude: 116.39, latitude: 39.9, zoom: 9, pitch: 0 },
controller: true,
layers: [basemap, dataLayer] // 先底图,后数据
});
{z}、{x}、{y} 会被替换为层级与瓦片行列号,顺序由瓦片服务决定。
XYZ 与 TMS 的 y 轴方向相反,拿到 TMS 服务时要写成
{z}/{x}/{-y}(部分版本支持反向索引),否则瓦片上下颠倒甚至 404。
瓦片服务的坐标系陷阱
绝大多数在线瓦片是 Web Mercator(EPSG:3857),经纬度直接对应
[lng, lat],无需换算。国内部分服务商(高德、腾讯)使用 GCJ-02
加密坐标,与 GPS 的 WGS-84 之间存在几十到上百米的偏移。
| 数据来源 | 坐标 | 直接叠加的后果 |
|---|---|---|
| 天地图、OSM、Esri | WGS-84 / CGCS2000 | 对齐正常 |
| 高德、腾讯瓦片 | GCJ-02 | GPS 点整体偏移数百米 |
| 百度瓦片 | BD-09 | 偏移更大,且投影参数不同 |
处理方式只有两条:把业务数据转换成与底图一致的加密坐标,或者 把加密底图反算回 WGS-84。前者适合只在国内展示的场景,后者适合 全球数据统一管理。无论选哪条,转换都必须发生在数据进入图层之前, deck.gl 不会替你做这一步。
叠加顺序与透明度
layers 数组的顺序就是绘制顺序。底图放第一位,
矢量数据放后面。若底图颜色太亮,可以在数据图层用半透明填充,
也可以在 BitmapLayer 上覆盖一层纯色 PolygonLayer
做压暗,比调瓦片样式更可控。
倾斜视角下,瓦片边缘可能出现细缝。把 tileSize 与瓦片实际
分辨率对齐(常见 256 或 512),并给 BitmapLayer 关闭
textureParameters 中的线性过滤,可以减轻接缝。
与 MapLibre 集成
MapLibre 负责底图、注记和相机,deck.gl 通过 MapboxOverlay
作为自定义图层插入。关键点是让 deck.gl 使用地图的
viewState,而不是自己再维护一套相机。
var overlay = new deck.MapboxOverlay({
interleaved: false, // 需要与三维建筑混合时设为 true
layers: [dataLayer]
});
map.addControl(overlay);
interleaved: true 让 deck.gl 与地图共用深度缓冲,
数据可以被建筑遮挡;代价是两侧渲染顺序强耦合,调试变复杂。
二维叠加场景保持 false 更稳。
与 Leaflet 联动的同步点
Leaflet 用 [纬度, 经度],deck.gl 用 [经度, 纬度]。
叠加时要监听 Leaflet 的 move 与 zoom 事件,
把中心点和层级换算成 deck.gl 的 viewState:
map.on('move', syncView);
function syncView() {
var c = map.getCenter();
deckgl.setProps({
viewState: {
longitude: c.lng,
latitude: c.lat,
zoom: map.getZoom() - 1, // Leaflet 与 deck.gl 的缩放级别差 1
pitch: 0,
bearing: 0
}
});
}
缩放级别差 1 是因为 Leaflet 的 0 级瓦片是 256 像素覆盖全球, deck.gl 的 0 级按 512 像素计算。不同版本可能调整,首次集成时 用同一城市的两个已知点校准一次更可靠。
内网与离线瓦片
内网部署时把 data 指向自建瓦片服务即可,格式仍是
{z}/{x}/{y}.png。若瓦片带鉴权,TileLayer
本身不附带请求头,需要在 renderSubLayers 里用自定义
loadOptions.fetch 注入头部,或让网关按来源 IP 放行。
先验证底图,再叠加数据。底图空白时不要急着调数据图层:
先确认瓦片请求返回 200 且图像不是透明空图,再检查
boundingBox 的经纬度顺序。两步都通过后,
数据不对基本只剩坐标偏移或投影不匹配两种原因。
经纬志(2026)。《与底图结合:瓦片、投影与偏移》。https://www.jingweizhi.com/deckgl/tutorial/basemap.html(访问日期 2026-11-03)。