0


Openlayers使用WebGL加载多种自定义图标矢量点

文章目录

概要

使用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>

效果:
加载100W矢量点效果

二、精灵图制作

TexturePackerGUI是一个非常实用的图形用户界面(GUI)工具,它能够将多张小图像合并成一张大图像,从而创建纹理图集。本文为便于编码和计算纹理坐标,使用TexturePackerGUI将精灵图强制处理成正方形。
制作规范:

  • 每个子图标应为大小一致的正方形(本文选用48×48)。
  • 图标共2500个,以0-49编号,放置于0-49编号的文件夹中。精灵图成图即为横向、纵向均为50个单位的图标阵列。
  • 编号0304即对应文件夹3中名称为4的图片,将你的图标替换对应编号文件夹中对应编号的图标。
  • 使用时将所有文件夹拖入TexturePackerGUI,按下方设置生成精灵图成图。

制作资源结构一览:
请添加图片描述
每个文件夹内:
请添加图片描述
TexturePackerGUI设置:
请添加图片描述
请添加图片描述

制作完成的精灵图长这样↓↓↓只在第一列放置了图标。
精灵图

三、生成矢量点数据

本文使用的矢量点数据为标准的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"}}}

四、生成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,请参考官方文档和示例以获取更新的表达式规则。

标签: webgl

本文转载自: https://blog.csdn.net/qq_40236953/article/details/134949648
版权归原作者 無可言喻 所有, 如有侵权,请联系我们删除。

“Openlayers使用WebGL加载多种自定义图标矢量点”的评论:

还没有评论