概要
优化OpenLayers的矢量加载,使用ImageCanvas图层代替VectorLayer加载矢量面(25W),并依旧可触发所有的VectorLayer地图交互事件。
整体思路
1.获取矢量图层,将多边形数据 polygon 添加到矢量图层 vectorLayer 的数据源中(触发地图交互的必要条件),并禁止矢量图层 vectorLayer 的渲染,确保其不会显示在地图上。
2.计算缩放比例,将地图坐标系转换为 Canvas 坐标系。
3.创建一个图像图层 imageLayer,使用 Canvas 进行渲染,并为其设置一个包含绘制逻辑的 canvasFunction,将图像图层 imageLayer 添加到地图中。
一、核心代码:ImageCanvas加载矢量面
<template>
<div ref="mapContainer" class="mapContainer" id="mapContainer"></div>
</template>
<script lang="ts" setup>
import { onMounted, shallowRef } from 'vue'
import { View, Map, Feature } from "ol"
import { fromLonLat } from 'ol/proj'
import TileLayer from 'ol/layer/Tile'
import { ImageCanvas, XYZ } from 'ol/source'
import { defaults as defaultControls } from "ol/control"
import { Image as CanvasLayer, Vector as VectorLayer } from 'ol/layer'
import VectorSource from 'ol/source/Vector'
import { Style, Fill, Stroke } from "ol/style";
import { FeatureLike } from 'ol/Feature'
import { Geometry, Polygon } from 'ol/geom'
import { Pixel } from 'ol/pixel'
import ImageSource from 'ol/source/Image'
import GeoJSON from 'ol/format/GeoJSON'
import { Size } from 'ol/size'
import type ImageLayer from "ol/layer/Image"
// 改为你自己的GeoJson数据地址
const polygonDataURL = './geoJson/polygon_25W'
//地图容器
const mapContainer = shallowRef<HTMLDivElement>()
//地图对象
const map = shallowRef<Map>()
/**
* @description 创建地图实例
* @param {Document | DocumentId} target 地图容器
* @returns 地图对象
*/
const createMap = function (target: HTMLElement | string,): Map {
// 创建地图
const map = new Map({
target,
layers: [
new TileLayer({
source: new XYZ({
url: "http://t0.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=99a8ea4a53c8553f6f3c565f7ffc15ec",
crossOrigin: 'anonymous',
wrapX: true
})
})
],
controls: defaultControls({
zoom: false, // 不显示放大放小按钮
rotate: false, // 不显示指北针控件
attribution: false, // 不显示右下角的地图信息控件
}).extend([]),
view: new View({
projection: 'EPSG:3857', // 坐标系EPSG:4326或EPSG:3857
zoom: 6, // 打开页面时默认地图缩放级别
maxZoom: 20,
minZoom: 1, // 地图缩放最小级别
center: fromLonLat([121.5, 25]), // 需要转到墨卡托坐标系
constrainResolution: true, // 自动缩放到距离最近的一个整数级别,因为当缩放在非整数级别时地图会糊
})
})
return map
}
/**
* @description 新建图层(检测到同名图层直接获取)
* @param map 地图实例
* @param layerName 图层名
* @param getStyle feature样式
* @returns
*/
function getVectorLayer(map: Map, layerName: String, getStyle: Function): VectorLayer<VectorSource> {
let vectorLayer = map.getLayers().getArray().find(layer => layer.get('name') === layerName) as VectorLayer<VectorSource>;
if (!vectorLayer) {
vectorLayer = new VectorLayer({
source: new VectorSource({ wrapX: true, features: [] }),
style: function (feature: FeatureLike) {
return getStyle(feature)
}
});
vectorLayer.set('name', layerName);
map.addLayer(vectorLayer);
}
return vectorLayer;
}
/**
* @description >>>>>> 添加多边形区域(ImageCanvas方式geojson格式)
* @param { Map } map 地图对象
* @param { string } layerName 图层名
*/
function addGeoPolygon_Img(
map: Map,
layerName: string,
polygonData: any
) {
if (!map || !polygonData || polygonData.length == 0) {
return;
}
// 获取矢量图层
let vectorLayer: VectorLayer<VectorSource> = getVectorLayer(
map,
layerName,
getStyle
);
// 禁止Vector渲染
vectorLayer.setVisible(false);
// 添加数据源
let features = new GeoJSON({
dataProjection: "EPSG:4326",
featureProjection: "EPSG:3857",
}).readFeatures(polygonData);
vectorLayer!.getSource()!.addFeatures(features);
// 地图大小
const mapsize = map.getSize() as Size;
// 使用canvas渲染
const imageLayer: ImageLayer<ImageSource> = new CanvasLayer({
source: new ImageCanvas({
canvasFunction: (_extent: any, _resolution: any, _pixelRatio: any, size: number[], _projection: any) => {
// canvas节点
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
// 设置画布大小
canvas.width = size[0];
canvas.height = size[1];
// 计算缩放比例
const ratio = mapsize[0] / size[0];
// 设置字体
ctx.font = "60px Arial";
// 绘制
features.forEach((feature: Feature<Geometry>) => {
// 每个格子设置颜色
const color = feature.get("color");
// 透明度,精度为0.01
const transparency = feature.get("transparency");
if (transparency !== ctx.globalAlpha) {
ctx.globalAlpha = transparency;
}
// 几何要素
let geometry = feature.getGeometry() as Polygon;
let coordinates = geometry.getCoordinates()[0];
// 开始绘制路径
ctx.beginPath();
// 遍历环中的每个点并绘制格子
let pixel: Pixel = [0, 0];
coordinates.forEach((point, index) => {
// 未转比例的坐标
const unconvertPixel = map.getPixelFromCoordinate(point);
// 转比例坐标
pixel = [unconvertPixel[0] / ratio, unconvertPixel[1] / ratio];
if (index === 0) {
// 将绘制起点移动到第一个点
ctx.moveTo(pixel[0], pixel[1]);
} else if (index === coordinates.length) {
// 绘制闭合路径
ctx.closePath();
} else {
// 绘制线段到下一个点
ctx.lineTo(pixel[0], pixel[1]);
}
});
ctx.fillStyle = color;
ctx.fill();
});
return canvas;
},
projection: "EPSG:3857",
ratio: 1,
}),
});
// canvas图层名字
imageLayer.set("name", layerName + "_canvas");
map.addLayer(imageLayer);
}
// 祥式
function getStyle(feature: Feature) {
const style = new Style({
// 填充
fill: new Fill({
color: 'rgb(0,255,0,0.2)',
}),
// 线框
stroke: new Stroke({
color: 'black',
})
});
return style;
}
onMounted(async () => {
map.value = createMap(mapContainer.value!);
// 打开GeoJson
const response = await fetch(`${polygonDataURL}.json`);
const polygon = await response.json();
// 加载Polygon
addGeoPolygon_Img(map.value, 'polygonLayer', polygon);
});
</script>
<style>
#mapContainer {
position: absolute;
top: 0;
z-index: 0;
width: 100%;
height: 100%;
}
</style>
加载效果:
二、数据说明
本文使用的矢量面数据为标准的GeoJson数据,数据及制作工具下方自取
数据样例:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"rotation": 0.47,
"name": "Wild Owl",
"opacity": 0.3,
"color": "#BC75AF"
},
"geometry": {
"coordinates": [
[
[100, 40],
[100,39.9],
[100.1,39.9],
[100.1,40],
[100,40]
]
],
"type": "Polygon"
}
}
]
}
- 矢量面数据(1W、25W)下载:https://download.csdn.net/download/qq_40236953/88632379
- 矢量面数据(GeoJson)生成工具:https://blog.csdn.net/qq_40236953/article/details/135014121