
3.1 瓦片图层
TIP
瓦片图层是 OpenLayers 中最常用的图层类型,用于显示栅格地图数据。本节将介绍各种瓦片源的使用方法和最新的 API 变更。
瓦片图层概述
瓦片图层(TileLayer)是 OpenLayers 中用于显示栅格数据的核心图层类型。它将地图数据分割成小的正方形瓦片,按需加载和显示,提供了高效的地图渲染性能。
瓦片系统的优势
- 高性能:按需加载,只显示当前视口内的瓦片
- 缓存友好:瓦片可以被浏览器和服务器缓存
- 可扩展性:支持多种瓦片服务和自定义瓦片源
- 标准化:遵循 OGC 标准(WMS、WMTS)和业界标准(XYZ)
基础瓦片图层
OpenStreetMap (OSM)
OpenStreetMap 是最常用的免费瓦片服务:
javascript
import TileLayer from 'ol/layer/Tile.js';
import OSM from 'ol/source/OSM.js';
const osmLayer = new TileLayer({
source: new OSM({
// 自定义属性信息
attributions: '© OpenStreetMap contributors',
// 最大缩放级别
maxZoom: 19,
// 跨域设置
crossOrigin: 'anonymous',
// 瓦片缓存大小
cacheSize: 512,
// 瓦片加载超时时间(毫秒)
tileLoadFunction: (tile, src) => {
const image = tile.getImage();
const timeout = setTimeout(() => {
console.warn('瓦片加载超时:', src);
tile.setState(3); // ERROR 状态
}, 10000);
image.onload = () => {
clearTimeout(timeout);
tile.setState(2); // LOADED 状态
};
image.onerror = () => {
clearTimeout(timeout);
tile.setState(3); // ERROR 状态
};
image.src = src;
}
})
});
XYZ 瓦片源
XYZ 是最通用的瓦片格式,支持自定义瓦片服务:
javascript
import XYZ from 'ol/source/XYZ.js';
const xyzLayer = new TileLayer({
source: new XYZ({
// 瓦片 URL 模板
url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
// 最大缩放级别
maxZoom: 18,
// 最小缩放级别
minZoom: 0,
// 瓦片大小
tileSize: [256, 256],
// 瓦片网格
tileGrid: createXYZ({
maxZoom: 18,
tileSize: 256
}),
// 属性信息
attributions: '© Custom Tile Service',
// 是否支持跨日期线
wrapX: true,
// ✅ 数据插值控制 (替代 imageSmoothing)
interpolate: true,
// ❌ 已弃用:tilePixelRatio (OpenLayers 6.15.0+)
// tilePixelRatio: 1,
// 过渡效果持续时间 (注意:VectorTile 源中此选项被忽略)
transition: 250
})
});
🆕 OpenLayers 10.x 瓦片源更新
重要 API 变更
已弃用的选项
javascript
// ❌ 已弃用:tilePixelRatio (OpenLayers 6.15.0+)
const oldDataTileSource = new DataTileSource({
tilePixelRatio: 2 // 已弃用
});
// ✅ 新方法:使用显式的 tileSize 和 tileGrid
const newDataTileSource = new DataTileSource({
tileSize: [512, 512], // 源瓦片尺寸
tileGrid: createXYZ({tileSize: [256, 256]}) // 渲染瓦片尺寸
});
interpolate 选项
javascript
// ✅ 新的插值控制选项 (替代 imageSmoothing)
const tileSource = new TileSource({
interpolate: false, // 禁用插值,获取原始像素值
// 对于 DataTile 源,默认为 false
// 对于其他源,默认为 true
});
现代瓦片源
StadiaMaps(替代 Stamen)
基于 Context7 验证,Stamen 已被 StadiaMaps 替代:
javascript
import StadiaMaps from 'ol/source/StadiaMaps.js';
// 水彩风格地图
const watercolorLayer = new TileLayer({
source: new StadiaMaps({
layer: 'stamen_watercolor',
// 注意:localhost 开发环境通常不需要 API key
// apiKey: 'YOUR_API_KEY' // 生产环境可能需要
})
});
// 地形标签图层
const terrainLabelsLayer = new TileLayer({
source: new StadiaMaps({
layer: 'stamen_terrain_labels',
// 可以与其他图层叠加使用
opacity: 0.8
})
});
// 可用的图层类型
const availableLayers = [
'stamen_watercolor', // 水彩风格
'stamen_terrain', // 地形图
'stamen_terrain_labels', // 地形标签
'stamen_toner', // 黑白风格
'stamen_toner_lite', // 轻量黑白
'outdoors', // 户外地图
'alidade_smooth', // 平滑风格
'alidade_smooth_dark' // 深色平滑
];
高德地图瓦片
javascript
const gaodeLayer = new TileLayer({
source: new XYZ({
url: 'https://webrd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
attributions: '© 高德地图'
})
});
天地图瓦片
javascript
const tiandituLayer = new TileLayer({
source: new XYZ({
url: 'https://t{0-7}.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=YOUR_API_KEY',
attributions: '© 天地图'
})
});
WMS 图层
Web Map Service (WMS) 是 OGC 标准的地图服务:
javascript
import TileWMS from 'ol/source/TileWMS.js';
const wmsLayer = new TileLayer({
source: new TileWMS({
url: 'https://ahocevar.com/geoserver/wms',
params: {
'LAYERS': 'ne:NE1_HR_LC_SR_W_DR',
'TILED': true,
'FORMAT': 'image/png',
'TRANSPARENT': true,
'VERSION': '1.1.1'
},
serverType: 'geoserver',
// OpenLayers 10.x 中 wrapX 默认为 true
wrapX: true,
// 投影设置
projection: 'EPSG:3857',
// 瓦片网格
tileGrid: createXYZ({
maxZoom: 18
})
})
});
// 动态更新 WMS 参数
function updateWMSParams(newParams) {
const source = wmsLayer.getSource();
source.updateParams(newParams);
// 或者完全替换参数(新的 API)
source.setParams({
...source.getParams(),
...newParams
});
}
WMTS 图层
Web Map Tile Service (WMTS) 是预渲染的瓦片服务:
javascript
import WMTS from 'ol/source/WMTS.js';
import WMTSTileGrid from 'ol/tilegrid/WMTS.js';
import { get as getProjection } from 'ol/proj.js';
const projection = getProjection('EPSG:3857');
const projectionExtent = projection.getExtent();
const size = getExtent(projectionExtent) / 256;
const resolutions = new Array(19);
const matrixIds = new Array(19);
for (let z = 0; z < 19; ++z) {
resolutions[z] = size / Math.pow(2, z);
matrixIds[z] = z;
}
const wmtsLayer = new TileLayer({
source: new WMTS({
url: 'https://services.arcgisonline.com/arcgis/rest/services/World_Topo_Map/MapServer/WMTS/',
layer: 'World_Topo_Map',
matrixSet: 'default028mm',
format: 'image/png',
projection: projection,
tileGrid: new WMTSTileGrid({
origin: getTopLeft(projectionExtent),
resolutions: resolutions,
matrixIds: matrixIds,
}),
style: 'default',
wrapX: true,
})
});
瓦片网格配置
自定义瓦片网格
javascript
import { createXYZ } from 'ol/tilegrid.js';
// 标准 XYZ 瓦片网格
const standardGrid = createXYZ({
maxZoom: 18,
tileSize: 256
});
// 高分辨率瓦片网格
const highResGrid = createXYZ({
maxZoom: 20,
tileSize: 512
});
// 自定义范围的瓦片网格
const customGrid = createXYZ({
extent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34],
maxZoom: 16,
tileSize: 256
});
性能优化
预加载策略
javascript
const optimizedLayer = new TileLayer({
source: new OSM(),
// 预加载级别
preload: 2,
// 在动画时更新
updateWhileAnimating: true,
// 在交互时更新
updateWhileInteracting: true,
// 使用中间瓦片
useInterimTilesOnError: true
});
缓存配置
javascript
const cachedLayer = new TileLayer({
source: new XYZ({
url: 'https://example.com/tiles/{z}/{x}/{y}.png',
// 缓存大小(瓦片数量)
cacheSize: 1024,
// 瓦片过期时间
tileLoadFunction: (tile, src) => {
const image = tile.getImage();
// 添加缓存控制头
const xhr = new XMLHttpRequest();
xhr.open('GET', src);
xhr.responseType = 'blob';
xhr.setRequestHeader('Cache-Control', 'max-age=3600');
xhr.onload = () => {
if (xhr.status === 200) {
const blob = xhr.response;
image.src = URL.createObjectURL(blob);
}
};
xhr.send();
}
})
});
最佳实践
错误处理和重试
javascript
class RobustTileLayer extends TileLayer {
constructor(options) {
super(options);
this.setupErrorHandling();
}
setupErrorHandling() {
const source = this.getSource();
source.on('tileloaderror', (event) => {
const tile = event.tile;
const tileCoord = tile.getTileCoord();
console.warn('瓦片加载失败:', tileCoord);
// 重试机制
this.retryTile(tile, 3);
});
}
retryTile(tile, maxRetries) {
let retryCount = 0;
const retry = () => {
if (retryCount < maxRetries) {
retryCount++;
console.log(`重试瓦片加载 (${retryCount}/${maxRetries})`);
setTimeout(() => {
tile.load();
}, 1000 * retryCount); // 递增延迟
} else {
console.error('瓦片加载最终失败');
// 可以设置默认瓦片或占位符
this.setPlaceholderTile(tile);
}
};
retry();
}
setPlaceholderTile(tile) {
const canvas = document.createElement('canvas');
canvas.width = 256;
canvas.height = 256;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#f0f0f0';
ctx.fillRect(0, 0, 256, 256);
ctx.fillStyle = '#999';
ctx.font = '16px Arial';
ctx.textAlign = 'center';
ctx.fillText('加载失败', 128, 128);
tile.getImage().src = canvas.toDataURL();
}
}
性能监控
javascript
class TilePerformanceMonitor {
constructor(layer) {
this.layer = layer;
this.stats = {
loaded: 0,
failed: 0,
totalLoadTime: 0,
averageLoadTime: 0
};
this.setupMonitoring();
}
setupMonitoring() {
const source = this.layer.getSource();
source.on('tileloadstart', (event) => {
event.tile.loadStartTime = performance.now();
});
source.on('tileloadend', (event) => {
const loadTime = performance.now() - event.tile.loadStartTime;
this.stats.loaded++;
this.stats.totalLoadTime += loadTime;
this.stats.averageLoadTime = this.stats.totalLoadTime / this.stats.loaded;
console.log(`瓦片加载完成: ${loadTime.toFixed(2)}ms`);
});
source.on('tileloaderror', (event) => {
this.stats.failed++;
console.warn('瓦片加载失败');
});
}
getStats() {
return {
...this.stats,
successRate: this.stats.loaded / (this.stats.loaded + this.stats.failed) * 100
};
}
logStats() {
const stats = this.getStats();
console.table(stats);
}
}
// 使用监控器
const monitor = new TilePerformanceMonitor(osmLayer);
setInterval(() => monitor.logStats(), 10000);
总结
瓦片图层是 OpenLayers 应用的基础,掌握各种瓦片源的使用方法对于构建高质量地图应用至关重要。本节介绍了:
- 基础瓦片源:OSM、XYZ 的标准用法
- 现代瓦片服务:StadiaMaps 替代 Stamen 的最新实践
- 标准服务:WMS、WMTS 的配置方法
- 性能优化:缓存、预加载、错误处理
- 监控调试:性能监控和问题诊断
注意事项
- 使用第三方瓦片服务时注意 API 限制和使用条款
- 生产环境中建议配置适当的错误处理和重试机制
- 注意瓦片服务的跨域设置和缓存策略
- StadiaMaps 在生产环境可能需要 API key