第03章 - 核心概念与架构设计

第03章 - 核心概念与架构设计

3.1 OpenLayers 设计理念

3.1.1 面向对象设计

OpenLayers 采用经典的面向对象设计模式,所有核心类都继承自基类,形成清晰的类层次结构:

┌─────────────────────────────────────────────────────────────┐
│                    OpenLayers 类继承体系                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   Disposable (可释放)                                       │
│       │                                                     │
│       └── Observable (可观察)                               │
│               │                                             │
│               └── BaseObject (基础对象)                     │
│                       │                                     │
│                       ├── Map                               │
│                       │                                     │
│                       ├── View                              │
│                       │                                     │
│                       ├── Layer (图层基类)                  │
│                       │     ├── TileLayer                   │
│                       │     ├── VectorLayer                 │
│                       │     ├── ImageLayer                  │
│                       │     └── VectorTileLayer             │
│                       │                                     │
│                       ├── Source (数据源基类)               │
│                       │     ├── TileSource                  │
│                       │     ├── VectorSource                │
│                       │     └── ImageSource                 │
│                       │                                     │
│                       ├── Feature                           │
│                       │                                     │
│                       ├── Interaction (交互基类)            │
│                       │     ├── DragPan                     │
│                       │     ├── Draw                        │
│                       │     └── Select                      │
│                       │                                     │
│                       └── Control (控件基类)                │
│                             ├── Zoom                        │
│                             ├── ScaleLine                   │
│                             └── Attribution                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3.1.2 可观察模式(Observable Pattern)

OpenLayers 的核心是可观察模式,所有对象都可以监听和触发事件:

import Observable from 'ol/Observable';

// Observable 类提供的基础能力
class MyClass extends Observable {
  constructor() {
    super();
    this.value = 0;
  }
  
  setValue(newValue) {
    const oldValue = this.value;
    this.value = newValue;
    // 触发自定义事件
    this.dispatchEvent({
      type: 'change:value',
      oldValue: oldValue,
      newValue: newValue
    });
  }
}

// 使用示例
const obj = new MyClass();

// 监听事件
obj.on('change:value', (event) => {
  console.log(`值从 ${event.oldValue} 变为 ${event.newValue}`);
});

// 触发事件
obj.setValue(100);

3.1.3 属性系统(Properties)

BaseObject 提供了属性管理系统:

import BaseObject from 'ol/Object';

// 创建可观察对象
const obj = new BaseObject({
  name: 'MyObject',
  visible: true,
  opacity: 1.0
});

// 获取属性
console.log(obj.get('name')); // 'MyObject'

// 设置属性
obj.set('opacity', 0.5);

// 获取所有属性
console.log(obj.getProperties()); // { name: 'MyObject', visible: true, opacity: 0.5 }

// 监听属性变化
obj.on('change:opacity', (event) => {
  console.log('透明度已更改:', obj.get('opacity'));
});

// 批量设置属性
obj.setProperties({
  name: 'NewName',
  visible: false
});

3.1.4 资源释放机制

OpenLayers 对象实现了 Disposable 接口,确保资源正确释放:

import Map from 'ol/Map';
import { unByKey } from 'ol/Observable';

// 创建地图
const map = new Map({
  target: 'map',
  // ...
});

// 添加事件监听,保存 key
const key = map.on('click', (event) => {
  console.log('点击');
});

// 移除单个事件监听
unByKey(key);

// 或使用 un 方法
const clickHandler = (event) => {
  console.log('点击');
};
map.on('click', clickHandler);
map.un('click', clickHandler);

// 销毁地图时释放所有资源
map.dispose();
// 或者设置 target 为 null
map.setTarget(null);

3.2 核心对象模型

3.2.1 Map 对象

Map 是 OpenLayers 的核心对象,负责管理所有地图组件:

import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import OSM from 'ol/source/OSM';
import { defaults as defaultControls } from 'ol/control';
import { defaults as defaultInteractions } from 'ol/interaction';

const map = new Map({
  // 挂载目标 DOM 元素
  target: 'map',
  
  // 图层集合
  layers: [
    new TileLayer({
      source: new OSM()
    })
  ],
  
  // 视图配置
  view: new View({
    center: [0, 0],
    zoom: 2
  }),
  
  // 控件(可选,有默认值)
  controls: defaultControls({
    attribution: true,
    zoom: true,
    rotate: false
  }),
  
  // 交互(可选,有默认值)
  interactions: defaultInteractions({
    doubleClickZoom: true,
    dragPan: true,
    mouseWheelZoom: true
  }),
  
  // 覆盖物集合
  overlays: [],
  
  // 其他配置
  pixelRatio: window.devicePixelRatio,
  moveTolerance: 1,
  maxTilesLoading: 16
});

Map 常用方法

// 图层管理
map.addLayer(layer);
map.removeLayer(layer);
map.getLayers(); // 返回 Collection

// 控件管理
map.addControl(control);
map.removeControl(control);
map.getControls();

// 交互管理
map.addInteraction(interaction);
map.removeInteraction(interaction);
map.getInteractions();

// 覆盖物管理
map.addOverlay(overlay);
map.removeOverlay(overlay);
map.getOverlays();

// 视图操作
map.getView();
map.setView(view);

// 坐标查询
map.getCoordinateFromPixel([x, y]);
map.getPixelFromCoordinate(coordinate);
map.getFeaturesAtPixel([x, y]);

// 渲染控制
map.render();
map.renderSync();
map.getSize();
map.updateSize();

// 目标元素
map.getTarget();
map.getTargetElement();
map.setTarget(element);

3.2.2 View 对象

View 管理地图的视图状态(中心点、缩放、旋转等):

import View from 'ol/View';
import { fromLonLat, toLonLat } from 'ol/proj';

const view = new View({
  // 中心点(Web Mercator 坐标)
  center: fromLonLat([116.4074, 39.9042]),
  
  // 缩放级别
  zoom: 10,
  
  // 或使用分辨率代替缩放
  // resolution: 1000,
  
  // 旋转角度(弧度)
  rotation: 0,
  
  // 投影
  projection: 'EPSG:3857',
  
  // 缩放限制
  minZoom: 2,
  maxZoom: 18,
  
  // 分辨率限制
  // minResolution: 0.5,
  // maxResolution: 10000,
  
  // 范围限制
  extent: undefined, // 或 [minX, minY, maxX, maxY]
  
  // 约束设置
  constrainRotation: true, // 旋转约束
  enableRotation: true,     // 允许旋转
  constrainOnlyCenter: false, // 仅约束中心点
  smoothExtentConstraint: true,
  smoothResolutionConstraint: true,
  
  // 缩放因子
  zoomFactor: 2,
  
  // 多分辨率支持
  multiWorld: false,
  
  // 显示边距
  padding: [0, 0, 0, 0] // [top, right, bottom, left]
});

// 设置到地图
map.setView(view);

View 常用方法

// 获取当前状态
view.getCenter();      // 获取中心点
view.getZoom();        // 获取缩放级别
view.getResolution();  // 获取分辨率
view.getRotation();    // 获取旋转角度
view.getProjection();  // 获取投影

// 设置状态
view.setCenter(center);
view.setZoom(zoom);
view.setResolution(resolution);
view.setRotation(rotation);

// 动画操作
view.animate({
  center: fromLonLat([121.4737, 31.2304]),
  zoom: 12,
  duration: 1000,
  easing: easeOut
});

// 适应范围
view.fit(extent, {
  size: map.getSize(),
  padding: [50, 50, 50, 50],
  duration: 500
});

// 适应几何
view.fit(geometry, {
  padding: [50, 50, 50, 50]
});

// 计算范围
view.calculateExtent(map.getSize());

// 约束检查
view.constrainCenter(center);
view.constrainResolution(resolution);
view.constrainRotation(rotation);

// 缩放操作
view.getZoomForResolution(resolution);
view.getResolutionForZoom(zoom);
view.getResolutionForExtent(extent, size);

3.2.3 Layer 对象

Layer 是图层的抽象基类,定义了所有图层的共同行为:

import TileLayer from 'ol/layer/Tile';
import VectorLayer from 'ol/layer/Vector';
import ImageLayer from 'ol/layer/Image';
import LayerGroup from 'ol/layer/Group';

// 瓦片图层
const tileLayer = new TileLayer({
  // 通用属性
  visible: true,           // 可见性
  opacity: 1.0,            // 透明度 (0-1)
  zIndex: 0,               // 堆叠顺序
  extent: undefined,       // 渲染范围
  minZoom: undefined,      // 最小缩放级别
  maxZoom: undefined,      // 最大缩放级别
  minResolution: undefined,// 最小分辨率
  maxResolution: undefined,// 最大分辨率
  className: 'ol-layer',   // CSS 类名
  
  // 数据源
  source: new OSM(),
  
  // 预加载
  preload: 0,
  
  // 使用临时画布
  useInterimTilesOnError: true,
  
  // 自定义属性
  properties: {
    name: 'OSM底图',
    type: 'basemap'
  }
});

// 矢量图层
const vectorLayer = new VectorLayer({
  source: vectorSource,
  style: styleFunction,
  
  // 矢量图层特有属性
  declutter: false,        // 去重叠
  renderBuffer: 100,       // 渲染缓冲区
  updateWhileAnimating: false,  // 动画时更新
  updateWhileInteracting: false // 交互时更新
});

// 图层组
const layerGroup = new LayerGroup({
  layers: [tileLayer, vectorLayer],
  visible: true,
  opacity: 1.0
});

Layer 常用方法

// 可见性
layer.getVisible();
layer.setVisible(true);

// 透明度
layer.getOpacity();
layer.setOpacity(0.8);

// 堆叠顺序
layer.getZIndex();
layer.setZIndex(10);

// 范围
layer.getExtent();
layer.setExtent(extent);

// 分辨率约束
layer.getMinResolution();
layer.getMaxResolution();
layer.setMinResolution(resolution);
layer.setMaxResolution(resolution);

// 缩放约束
layer.getMinZoom();
layer.getMaxZoom();
layer.setMinZoom(zoom);
layer.setMaxZoom(zoom);

// 数据源
layer.getSource();
layer.setSource(source);

// 自定义属性
layer.get('name');
layer.set('name', 'NewName');
layer.getProperties();

// 获取所属地图
layer.getMapInternal();

3.2.4 Source 对象

Source 负责管理图层的数据来源:

import OSM from 'ol/source/OSM';
import XYZ from 'ol/source/XYZ';
import TileWMS from 'ol/source/TileWMS';
import WMTS from 'ol/source/WMTS';
import VectorSource from 'ol/source/Vector';
import ImageWMS from 'ol/source/ImageWMS';

// XYZ 瓦片源
const xyzSource = new XYZ({
  url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
  maxZoom: 19,
  crossOrigin: 'anonymous',
  attributions: '© OpenStreetMap contributors',
  
  // 瓦片相关配置
  tileSize: [256, 256],
  tilePixelRatio: 1,
  cacheSize: 2048,
  
  // 错误处理
  reprojectionErrorThreshold: 0.5
});

// WMS 瓦片源
const wmsSource = new TileWMS({
  url: 'http://localhost:8080/geoserver/wms',
  params: {
    'LAYERS': 'workspace:layer',
    'TILED': true,
    'FORMAT': 'image/png'
  },
  serverType: 'geoserver',
  crossOrigin: 'anonymous'
});

// 矢量数据源
const vectorSource = new VectorSource({
  url: 'data/geojson/points.geojson',
  format: new GeoJSON(),
  
  // 加载策略
  strategy: bboxStrategy, // 或 allStrategy, tileStrategy
  
  // 数据加载器
  loader: function(extent, resolution, projection) {
    // 自定义加载逻辑
  },
  
  // 属性名映射
  attributions: '数据来源',
  
  // 叠加模式
  overlaps: true,
  
  // 数据缓存
  useSpatialIndex: true,
  
  // 数据包裹
  wrapX: true
});

Source 常用方法

// 通用方法
source.getState();      // 'undefined' | 'loading' | 'ready' | 'error'
source.getAttributions();
source.getProjection();
source.refresh();

// 矢量源特有方法
vectorSource.addFeature(feature);
vectorSource.addFeatures(features);
vectorSource.removeFeature(feature);
vectorSource.clear();
vectorSource.getFeatures();
vectorSource.getFeatureById(id);
vectorSource.getFeaturesAtCoordinate(coordinate);
vectorSource.getFeaturesInExtent(extent);
vectorSource.forEachFeature(callback);
vectorSource.getExtent();
vectorSource.isEmpty();
vectorSource.getClosestFeatureToCoordinate(coordinate);

// 事件监听
vectorSource.on('addfeature', (event) => {
  console.log('添加要素:', event.feature);
});

vectorSource.on('removefeature', (event) => {
  console.log('移除要素:', event.feature);
});

vectorSource.on('change', () => {
  console.log('数据源变化');
});

3.2.5 Feature 对象

Feature 表示地图上的单个要素:

import Feature from 'ol/Feature';
import Point from 'ol/geom/Point';
import LineString from 'ol/geom/LineString';
import Polygon from 'ol/geom/Polygon';
import { fromLonLat } from 'ol/proj';
import { Style, Fill, Stroke, Circle, Icon, Text } from 'ol/style';

// 创建点要素
const pointFeature = new Feature({
  geometry: new Point(fromLonLat([116.4074, 39.9042])),
  name: '北京',
  population: 21540000
});

// 设置 ID
pointFeature.setId('beijing');

// 设置样式
pointFeature.setStyle(new Style({
  image: new Circle({
    radius: 10,
    fill: new Fill({ color: 'red' }),
    stroke: new Stroke({ color: 'white', width: 2 })
  }),
  text: new Text({
    text: '北京',
    offsetY: -20,
    font: '14px sans-serif',
    fill: new Fill({ color: '#333' })
  })
}));

// 创建线要素
const lineFeature = new Feature({
  geometry: new LineString([
    fromLonLat([116.4074, 39.9042]),
    fromLonLat([121.4737, 31.2304])
  ]),
  name: '北京-上海'
});

// 创建面要素
const polygonFeature = new Feature({
  geometry: new Polygon([[
    fromLonLat([116.0, 39.0]),
    fromLonLat([117.0, 39.0]),
    fromLonLat([117.0, 40.0]),
    fromLonLat([116.0, 40.0]),
    fromLonLat([116.0, 39.0])
  ]]),
  name: '区域A'
});

Feature 常用方法

// ID 管理
feature.getId();
feature.setId(id);

// 几何操作
feature.getGeometry();
feature.setGeometry(geometry);
feature.getGeometryName();
feature.setGeometryName(name);

// 样式操作
feature.getStyle();
feature.setStyle(style);
feature.getStyleFunction();

// 属性操作
feature.get('name');
feature.set('name', value);
feature.getProperties();
feature.setProperties(properties);
feature.unset('name');

// 克隆
const cloned = feature.clone();

// 事件
feature.on('change', () => {
  console.log('要素变化');
});

feature.on('change:geometry', () => {
  console.log('几何变化');
});

3.3 事件系统

3.3.1 事件类型

OpenLayers 提供丰富的事件类型:

// Map 事件
map.on('click', (event) => {});         // 点击
map.on('dblclick', (event) => {});      // 双击
map.on('singleclick', (event) => {});   // 单击(无双击跟随)
map.on('pointermove', (event) => {});   // 指针移动
map.on('pointerdrag', (event) => {});   // 指针拖拽
map.on('movestart', (event) => {});     // 移动开始
map.on('moveend', (event) => {});       // 移动结束
map.on('postrender', (event) => {});    // 渲染完成
map.on('precompose', (event) => {});    // 渲染前
map.on('postcompose', (event) => {});   // 合成后
map.on('rendercomplete', (event) => {}); // 完全渲染完成

// View 事件
view.on('change:center', (event) => {}); // 中心点变化
view.on('change:zoom', (event) => {});   // 缩放变化
view.on('change:rotation', (event) => {}); // 旋转变化
view.on('change:resolution', (event) => {}); // 分辨率变化

// Layer 事件
layer.on('change:visible', (event) => {}); // 可见性变化
layer.on('change:opacity', (event) => {}); // 透明度变化
layer.on('change:source', (event) => {});  // 数据源变化

// Source 事件
source.on('change', (event) => {});        // 数据变化
source.on('addfeature', (event) => {});    // 添加要素
source.on('removefeature', (event) => {}); // 移除要素
source.on('changefeature', (event) => {}); // 要素变化
source.on('clear', (event) => {});         // 清空

// Collection 事件
collection.on('add', (event) => {});       // 添加元素
collection.on('remove', (event) => {});    // 移除元素

3.3.2 事件对象

// MapBrowserEvent(地图浏览器事件)
map.on('click', (event) => {
  // 基本属性
  console.log(event.type);           // 事件类型
  console.log(event.map);            // 地图实例
  console.log(event.coordinate);     // 地图坐标
  console.log(event.pixel);          // 像素坐标
  console.log(event.originalEvent);  // 原生 DOM 事件
  console.log(event.dragging);       // 是否拖拽中
  
  // 功能键状态
  const origEvent = event.originalEvent;
  console.log(origEvent.ctrlKey);    // Ctrl 键
  console.log(origEvent.shiftKey);   // Shift 键
  console.log(origEvent.altKey);     // Alt 键
});

// 要素查询
map.on('click', (event) => {
  // 获取点击位置的要素
  map.forEachFeatureAtPixel(event.pixel, (feature, layer) => {
    console.log('点击要素:', feature.get('name'));
    console.log('所属图层:', layer);
    return true; // 返回 true 停止遍历
  });
  
  // 检查是否有要素
  const hasFeature = map.hasFeatureAtPixel(event.pixel);
  
  // 获取所有要素
  const features = map.getFeaturesAtPixel(event.pixel);
});

3.3.3 事件监听管理

import { unByKey } from 'ol/Observable';

// 添加事件监听
const key = map.on('click', clickHandler);

// 添加一次性事件监听
const onceKey = map.once('click', (event) => {
  console.log('只触发一次');
});

// 移除事件监听 - 方式1:使用 key
unByKey(key);

// 移除事件监听 - 方式2:使用 un 方法
map.un('click', clickHandler);

// 批量添加事件监听
const keys = [
  map.on('click', clickHandler),
  map.on('pointermove', moveHandler),
  map.on('moveend', moveEndHandler)
];

// 批量移除
keys.forEach(key => unByKey(key));

// 使用事件管理器类
class EventManager {
  constructor() {
    this.keys = [];
  }
  
  add(target, type, handler) {
    const key = target.on(type, handler);
    this.keys.push(key);
    return key;
  }
  
  remove(key) {
    const index = this.keys.indexOf(key);
    if (index > -1) {
      this.keys.splice(index, 1);
      unByKey(key);
    }
  }
  
  clear() {
    this.keys.forEach(key => unByKey(key));
    this.keys = [];
  }
}

const eventManager = new EventManager();
eventManager.add(map, 'click', clickHandler);
eventManager.add(map, 'moveend', moveEndHandler);

// 清理所有事件
eventManager.clear();

3.3.4 自定义事件

import BaseObject from 'ol/Object';

// 创建自定义可观察对象
class CustomMapTool extends BaseObject {
  constructor() {
    super();
    this.active = false;
  }
  
  activate() {
    this.active = true;
    this.dispatchEvent({
      type: 'activate',
      tool: this
    });
  }
  
  deactivate() {
    this.active = false;
    this.dispatchEvent({
      type: 'deactivate',
      tool: this
    });
  }
  
  complete(result) {
    this.dispatchEvent({
      type: 'complete',
      result: result
    });
  }
}

// 使用自定义工具
const tool = new CustomMapTool();

tool.on('activate', (event) => {
  console.log('工具已激活');
});

tool.on('deactivate', (event) => {
  console.log('工具已停用');
});

tool.on('complete', (event) => {
  console.log('操作完成:', event.result);
});

tool.activate();
tool.complete({ feature: newFeature });
tool.deactivate();

3.4 集合管理(Collection)

3.4.1 Collection 基础

import Collection from 'ol/Collection';

// 创建集合
const collection = new Collection();

// 带初始值创建
const collection2 = new Collection([item1, item2, item3]);

// 添加元素
collection.push(item);
collection.insertAt(0, item);
collection.extend([item1, item2]);

// 移除元素
collection.remove(item);
collection.removeAt(0);
collection.pop();
collection.clear();

// 访问元素
collection.item(0);
collection.getArray();
collection.getLength();
collection.forEach((item, index) => {});

// 查找
collection.indexOf(item);

// 集合事件
collection.on('add', (event) => {
  console.log('添加元素:', event.element);
});

collection.on('remove', (event) => {
  console.log('移除元素:', event.element);
});

collection.on('change:length', () => {
  console.log('集合长度变化');
});

3.4.2 图层集合

// 获取图层集合
const layers = map.getLayers();

// 添加图层
layers.push(newLayer);
layers.insertAt(0, baseLayer);

// 移除图层
layers.remove(layer);

// 调整顺序
const index = layers.getArray().indexOf(layer);
layers.removeAt(index);
layers.insertAt(newIndex, layer);

// 遍历图层
layers.forEach((layer, index) => {
  console.log(`图层 ${index}:`, layer.get('name'));
});

// 监听图层变化
layers.on('add', (event) => {
  console.log('添加图层:', event.element);
});

layers.on('remove', (event) => {
  console.log('移除图层:', event.element);
});

// 查找图层
function findLayerByName(name) {
  let found = null;
  layers.forEach((layer) => {
    if (layer.get('name') === name) {
      found = layer;
    }
  });
  return found;
}

3.4.3 要素集合

import Collection from 'ol/Collection';
import Feature from 'ol/Feature';

// 创建要素集合(用于 Select 交互)
const selectedFeatures = new Collection();

// 绑定到 Select 交互
const selectInteraction = new Select({
  features: selectedFeatures
});

// 监听选择变化
selectedFeatures.on('add', (event) => {
  console.log('选中要素:', event.element);
});

selectedFeatures.on('remove', (event) => {
  console.log('取消选中:', event.element);
});

// 程序化操作选择
selectedFeatures.clear(); // 清除所有选择
selectedFeatures.push(feature); // 添加选择

3.5 投影系统基础

3.5.1 内置投影

import { get as getProjection, transform, fromLonLat, toLonLat } from 'ol/proj';

// 获取投影对象
const wgs84 = getProjection('EPSG:4326');     // WGS84 经纬度
const webMercator = getProjection('EPSG:3857'); // Web Mercator

// 检查投影属性
console.log(webMercator.getCode());      // 'EPSG:3857'
console.log(webMercator.getUnits());     // 'm' (米)
console.log(webMercator.getExtent());    // 全球范围
console.log(webMercator.isGlobal());     // true
console.log(webMercator.getMetersPerUnit()); // 1

// 经纬度转 Web Mercator
const webMercatorCoord = fromLonLat([116.4074, 39.9042]);
// 结果: [12958752.49..., 4853167.35...]

// Web Mercator 转经纬度
const lonLatCoord = toLonLat([12958752.49, 4853167.35]);
// 结果: [116.4074..., 39.9042...]

// 通用坐标转换
const coord = transform(
  [116.4074, 39.9042],
  'EPSG:4326',
  'EPSG:3857'
);

3.5.2 注册自定义投影

import { register } from 'ol/proj/proj4';
import proj4 from 'proj4';

// 定义投影(以 CGCS2000 为例)
proj4.defs('EPSG:4490', '+proj=longlat +ellps=GRS80 +no_defs');

// 定义高斯投影
proj4.defs('EPSG:4527', '+proj=tmerc +lat_0=0 +lon_0=117 +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +units=m +no_defs');

// 注册到 OpenLayers
register(proj4);

// 使用自定义投影
import { get as getProjection } from 'ol/proj';

const cgcs2000 = getProjection('EPSG:4490');
console.log(cgcs2000.getCode()); // 'EPSG:4490'

3.6 渲染机制

3.6.1 渲染流程

┌─────────────────────────────────────────────────────────────┐
│                    OpenLayers 渲染流程                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   1. 触发渲染                                                │
│      │                                                      │
│      ├── 视图变化(平移、缩放、旋转)                         │
│      ├── 图层变化(添加、移除、可见性)                       │
│      ├── 数据变化(要素增删改)                              │
│      └── 手动调用 map.render()                              │
│                                                             │
│   2. 准备阶段                                                │
│      │                                                      │
│      ├── 计算当前视口范围                                    │
│      ├── 确定需要渲染的图层                                  │
│      └── 请求瓦片/要素数据                                   │
│                                                             │
│   3. 渲染阶段                                                │
│      │                                                      │
│      ├── 按 zIndex 排序图层                                  │
│      ├── 逐图层渲染                                          │
│      │     ├── 瓦片图层:绘制瓦片                            │
│      │     ├── 矢量图层:应用样式绘制要素                     │
│      │     └── 图像图层:绘制图像                            │
│      └── 合成最终图像                                        │
│                                                             │
│   4. 后处理                                                  │
│      │                                                      │
│      ├── 触发 postrender 事件                                │
│      └── 触发 rendercomplete 事件                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3.6.2 Canvas 渲染

// Canvas 渲染器是默认选项
// 每个图层在单独的 Canvas 上渲染

// 获取渲染器
const renderer = map.getRenderer();

// 渲染事件钩子
map.on('precompose', (event) => {
  // 渲染前,可以访问 Canvas 上下文
  const ctx = event.context;
  ctx.save();
});

map.on('postcompose', (event) => {
  // 渲染后,可以进行后处理
  const ctx = event.context;
  // 自定义绘制
  ctx.restore();
});

// 手动渲染到 Canvas
map.on('rendercomplete', () => {
  const canvas = document.createElement('canvas');
  const size = map.getSize();
  canvas.width = size[0];
  canvas.height = size[1];
  
  const context = canvas.getContext('2d');
  
  // 合成所有图层
  document.querySelectorAll('.ol-layer canvas').forEach((layerCanvas) => {
    if (layerCanvas.width > 0) {
      const opacity = layerCanvas.parentNode.style.opacity || 1;
      context.globalAlpha = opacity;
      
      const transform = layerCanvas.style.transform;
      // 应用变换...
      
      context.drawImage(layerCanvas, 0, 0);
    }
  });
  
  // 导出图片
  const dataUrl = canvas.toDataURL('image/png');
});

3.6.3 WebGL 渲染

import WebGLPointsLayer from 'ol/layer/WebGLPoints';

// 使用 WebGL 渲染大量点数据
const webglLayer = new WebGLPointsLayer({
  source: new VectorSource({
    features: generateManyPoints(100000)
  }),
  style: {
    symbol: {
      symbolType: 'circle',
      size: 8,
      color: 'red',
      opacity: 0.8
    }
  }
});

map.addLayer(webglLayer);

// WebGL 样式表达式
const styleWithExpression = {
  symbol: {
    symbolType: 'circle',
    size: ['interpolate', ['linear'], ['get', 'value'], 0, 5, 100, 20],
    color: [
      'interpolate',
      ['linear'],
      ['get', 'value'],
      0, 'green',
      50, 'yellow',
      100, 'red'
    ],
    opacity: 0.8
  }
};

3.7 本章小结

本章深入介绍了 OpenLayers 的核心概念与架构设计:

  1. 设计理念:面向对象、可观察模式、属性系统、资源释放
  2. 核心对象:Map、View、Layer、Source、Feature
  3. 事件系统:事件类型、事件对象、事件管理
  4. 集合管理:Collection、图层集合、要素集合
  5. 投影系统:内置投影、自定义投影
  6. 渲染机制:渲染流程、Canvas 渲染、WebGL 渲染

关键要点

  • 所有核心类都继承自 Observable,支持事件监听
  • 使用 BaseObject 的属性系统管理配置
  • 正确管理事件监听器避免内存泄漏
  • 理解 Collection 在图层和要素管理中的作用
  • 掌握 Canvas 和 WebGL 两种渲染方式的适用场景

下一步

在下一章中,我们将详细学习 Map 地图对象,包括:

  • Map 创建与配置
  • 地图方法详解
  • 地图事件处理
  • 地图导出与打印

← 上一章:环境搭建与快速开始 | 返回目录 | 下一章:Map地图对象详解 →

posted @ 2026-01-08 11:37  我才是银古  阅读(14)  评论(0)    收藏  举报