文章目录
概要
使用OpenLayers的WebGLPointsLayer加载海量矢量点(100W),并使用多种自定义图标样式。
你需要满足以下环境要求:
- 支持WebGL的浏览器(Chrome、Firefox、Safari和Edge等)。
- 计算机具有一定的图形处理能力(显卡)。
- 一定的计算机内存和网络带宽。
- 一点耐心
整体思路
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 地图对象
*/constcreateMap=function(target: HTMLElement |string,): Map {// 创建地图const map =newMap({
target,
layers:[newTileLayer({
source:newXYZ({
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:newView({
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格式数据
*/functionaddPointVector_WebGL(map: Map, pointData:any){// 添加数据源let features =newGeoJSON({
dataProjection:"EPSG:4326",
featureProjection:"EPSG:3857",}).readFeatures(pointData)as Feature<Point>[];// 创建WebGL图层const webGLLayer =newWebGLPointsLayer({
source:newVectorSource<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将精灵图强制处理成正方形。
制作规范:
- 每个子图标应为大小一致的正方形(本文选用48×48)。
- 图标共2500个,以0-49编号,放置于0-49编号的文件夹中。精灵图成图即为横向、纵向均为50个单位的图标阵列。
- 编号0304即对应文件夹3中名称为4的图片,将你的图标替换对应编号文件夹中对应编号的图标。
- 使用时将所有文件夹拖入TexturePackerGUI,按下方设置生成精灵图成图。
制作资源结构一览:
每个文件夹内:
TexturePackerGUI设置:
- 精灵图工具(TexturePackerGUI)下载:https://pan.baidu.com/s/1ymvpArEZ8eKVx9M0QWx3pg?pwd=scas 提取码:scas
- 精灵图及制作资源下载地址:https://download.csdn.net/download/qq_40236953/88625155
制作完成的精灵图长这样↓↓↓只在第一列放置了图标。
三、生成矢量点数据
本文使用的矢量点数据为标准的GeoJson数据,数据及制作工具下方自取,属性字段包括:
- name:名称/编码,类型为 string | number,对应文件夹+图片名称编码
- rotationx:旋转角度,范围0~1
- englishType:分类,范围0~1(不接受字符串,啊,难受)
- opacity:透明度,范围0~1
- color:颜色匹配值,类型为 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/88625008
- textureCoord(match表达式)代码地址:https://blog.csdn.net/qq_40236953/article/details/134977028
版权归原作者 無可言喻 所有, 如有侵权,请联系我们删除。