文章目录
概要整体思路一、核心代码:WebGLPointsLayer加载矢量点二、精灵图制作三、生成矢量点数据四、生成match表达式
概要
使用OpenLayers的WebGLPointsLayer加载海量矢量点(100W),并使用多种自定义图标样式。
你需要满足以下环境要求:
整体思路
1.创建一个包含所有图标的精灵图,每个图标在精灵图中占据一个正方形区域。精灵图可以使用简单的等分布局,也可以根据实际需求使用其他布局算法。
2.WebGLPointsLayer的Style函数无法获取返回对象feature,但可以在表达式中返回部分属性值。可以根据每个矢量点的属性值(例如图标类型),在映射文件中找到对应图标在精灵图中的纹理坐标。
3.将得到的纹理坐标赋值给每个矢量点的Style函数。这样,WebGLPointsLayer就可以根据纹理坐标来渲染每个点的图标。
4.在渲染过程中,WebGLPointsLayer会根据每个点的纹理坐标从精灵图中获取对应的图标部分,并将其渲染到屏幕上。
一、核心代码:WebGLPointsLayer加载矢量点
<template> <div ref="mapContainer" class="mapContainer" id="mapContainer"></div></template><script lang="ts" setup>import "ol/ol.css";import { Feature, Map, View } from "ol";import { Point } from "ol/geom";import WebGLPointsLayer from "ol/layer/WebGLPoints";import { Vector as VectorSource, } from "ol/source";import GeoJSON from "ol/format/GeoJSON";import { fromLonLat } from "ol/proj";import TileLayer from "ol/layer/Tile";import { onMounted, shallowRef } from "vue";import { XYZ } from 'ol/source'import { defaults as defaultControls } from "ol/control"// 改为你的GeoJson数据地址const pointsDataURL = './geoJson/20W'// 改为你的精灵图地址const imageSrc = './public/image/texture.png'// 地图容器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: false }) }) ], 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 >>>>>>>> 地图上添加矢量点(WebGL方式) * @param { Map } map 地图对象 * @param { any } pointData GeoJson格式数据 */function addPointVector_WebGL(map: Map, pointData: any) { // 添加数据源 let features = new GeoJSON({ dataProjection: "EPSG:4326", featureProjection: "EPSG:3857", }).readFeatures(pointData) as Feature<Point>[]; // 创建WebGL图层 const webGLLayer = new WebGLPointsLayer({ source: new VectorSource<Point>({ features: features, // yourFeatures是包含要素的数组 }), style: { symbol: { symbolType: "image", rotation: ["get", "rotation"], src: imageSrc, size: [32, 32], color: [ "match", ["get", "color"], 0, "red", 1, "blue", 2, "green", 3, "pink", // 默认颜色 "black", ], opacity: ["get", "opacity"], rotateWithView: false, /** * @description textureCoord纹理坐标使用百分比切割图 * 每次在横纵方向移动一个单位(1/横向排列图标数量||1/纵向排列图标数量)获取下一个纹理坐标 */ textureCoord: [ "match", ["get", "englishType"], // get返回0-1之间的数 0.00, textureCoordData.a, 0.01, textureCoordData.b, 0.02, textureCoordData.c, 0.03, textureCoordData.d, 0.04, textureCoordData.e, 0.05, textureCoordData.f, 0.06, textureCoordData.g, 0.07, textureCoordData.h, 0.08, textureCoordData.i, 0.09, textureCoordData.j, 0.10, textureCoordData.k, 0.11, textureCoordData.l, 0.12, textureCoordData.m, 0.13, textureCoordData.n, 0.14, textureCoordData.o, 0.15, textureCoordData.p, 0.16, textureCoordData.q, 0.17, textureCoordData.r, 0.18, textureCoordData.s, 0.19, textureCoordData.t, 0.20, textureCoordData.u, 0.21, textureCoordData.v, 0.22, textureCoordData.w, 0.23, textureCoordData.x, 0.24, textureCoordData.y, //默认 textureCoordData.z, ], }, }, disableHitDetection: true, // 禁用命中检测以提高性能 }); map.addLayer(webGLLayer);}// 由于篇幅没有放出完整的textureCoord,后续有数据下载链接和表达式生成代码const textureCoordData = { "x": [ "match", ["get", "name"], "4600", [0, 0.92, 0.02, 0.94], "4700", [0, 0.94, 0.02, 0.96], [0.98, 0.98, 1, 1] ], "y": [ "match", ["get", "name"], // "4801",// 假设4801和4802没有纹理,位置的坐标和编码应注释掉 // [0.02, 0.96, 0.04, 0.98], // "4802", // [0.04, 0.96, 0.06, 0.98], "4800", [0, 0.96, 0.02, 0.98], "4900", [0, 0.98, 0.02, 1], [0.98, 0.98, 1, 1] ], "z": [0.98, 0.98, 1, 1]}onMounted(() => { // 创建地图 map.value = createMap(mapContainer.value) // 打开GeoJson数据 fetch(`${pointsDataURL}.json`) .then(response => response.json()) .then(pointsData => { // 加载矢量点 addPointVector_WebGL(map, pointsData) })})</script><style>#mapContainer { position: absolute; top: 0; z-index: 0; width: 100%; height: 100%;}</style>
效果:
二、精灵图制作
TexturePackerGUI是一个非常实用的图形用户界面(GUI)工具,它能够将多张小图像合并成一张大图像,从而创建纹理图集。本文为便于编码和计算纹理坐标,使用TexturePackerGUI将精灵图强制处理成正方形。
制作规范:
制作资源结构一览:
每个文件夹内:
TexturePackerGUI设置:
提取码:scas精灵图及制作资源下载地址:https://download.csdn.net/download/qq_40236953/88625155
制作完成的精灵图长这样↓↓↓只在第一列放置了图标。
三、生成矢量点数据
本文使用的矢量点数据为标准的GeoJson数据,数据及制作工具下方自取,属性字段包括:
name:名称/编码,类型为 string | number,对应文件夹+图片名称编码rotationx:旋转角度,范围0~1englishType:分类,范围0~1(不接受字符串,啊,难受)opacity:透明度,范围0~1color:颜色匹配值,类型为 number(依然不接受字符串、rgb色值,所以以match匹配值的方式实现颜色区分)数据样例:
{ "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": { "rotation": 0.01, "name": "3500", "englishType": 0.17, "opacity": 0.4, "color": 2 }, "geometry": { "coordinates": [ -15.183829600909434, 63.574883642733454 ], "type": "Point" } }}
矢量点数据(10W、50W、100W)下载:https://download.csdn.net/download/qq_40236953/88622814矢量点数据(GeoJson)生成工具:https://blog.csdn.net/qq_40236953/article/details/134954087 四、生成match表达式
测试出来的特性:
1.textureCoord使用的match表达式,数组的长度有限制,大约可以支持500个元素。如果只使用一层match,那么最多只能匹配大约250个不同的图标。
2.match表达式允许嵌套,一层嵌套可以看作是要素的一级分类,比如汽车、飞机、船,而二层嵌套则是详细分类,比如公交车、轿车、快递车(嵌套层级无限制)。理论上嵌套后图标数目不受限,因此在textureCoordData中使用了多层嵌套的结构。
嵌套match表达式解析:
// 为了便于理解,把二级match表达式的匹配项代入一级表达式 [ "match",// 指示使用match表达式规则进行匹配 ["get", "englishType"], // 获取属性字段名称为"englishType"的属性值 0.23, // 一次匹配,匹配值为0.23时 // 0.23的匹配结果是一个match表达式 ["match", ["get", "name"],// 获取属性字段名称为"name"的属性值 "4600",// 二次匹配,匹配值为"4600"时 [0, 0.92, 0.02, 0.94],// 匹配成功,将应用的纹理坐标 "4700",// 二次匹配,匹配值为"4700"时 [0, 0.94, 0.02, 0.96],// 匹配成功,将应用的纹理坐标 [0.98, 0.98, 1, 1]// 匹配失败,应用的纹理坐标(默认) ], 0.24, // 0.24的匹配结果是一个match表达式 ["match", ["get", "name"],// 二次匹配 "4800",// 匹配值 [0, 0.96, 0.02, 0.98],// 匹配结果 "4900",// 匹配值 [0, 0.98, 0.02, 1],// 匹配结果 [0.98, 0.98, 1, 1]// 匹配失败,默认值 ] [0.98, 0.98, 1, 1]// 匹配失败,默认值 ],
3.过于复杂的match表达式可能会导致性能问题,因此不宜将嵌套层数/表达式选项设置过多。
4.textureCoordData中,应注释掉没有实际使用的图标纹理坐标和匹配值,以节省性能。
如下图,假设编号4801和4802没有纹理,位置的坐标和编码应注释掉↓↓↓:
const textureCoordData = { "y": [ "match", ["get", "name"], // 假设4801没有纹理,位置的坐标和编码应注释掉 // "4801", // [0.02, 0.96, 0.04, 0.98], // 假设4802没有纹理,位置的坐标和编码应注释掉 // "4802", // [0.04, 0.96, 0.06, 0.98], "4800", [0, 0.96, 0.02, 0.98], "4900", [0, 0.98, 0.02, 1], [0.98, 0.98, 1, 1] ], "z": [0.98, 0.98, 1, 1]}
5.[“get”, “name”]可以返回number或string类型的值,但是对于[“get”, “其他键名”],它只能返回0到1之间的小数。因此,[“get”, “englishType”]的匹配值是诸如0.01这样的小数。
6.OpenLayers 7中的textureCoord表达式与OpenLayers 6稍有不同(纹理坐标),具体的用法和语法可能会有所区别。如果使用OpenLayers 6,请参考官方文档和示例以获取更新的表达式规则。
textureCoord(match表达式)下载地址:https://download.csdn.net/download/qq_40236953/88625008textureCoord(match表达式)代码地址:https://blog.csdn.net/qq_40236953/article/details/134977028