Skip to content

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

如有转载或 CV 的请标注本站原文地址