当前位置: 首页 > news >正文

MapLibre GL JS第34课:使用addProtocol转换要素属性

📌 学习目标

  • 掌握使用addProtocol转换要素属性的实现方法
  • 理解相关API的使用
  • 能够独立完成类似功能开发

🎯 核心概念

在纯JavaScript中使用addProtocol反转国家名称。

💻 完 整 代 码

代码示例

importProtobuffrom'https://unpkg.com/pbf@4.0.1/index.js';import{VectorTile}from'https://esm.run/@mapbox/vector-tile@2.0.3/index.js';importtileToProtobuffrom'https://esm.run/vt-pbf@3.1.3/index.js';constprotocol='reverse';maplibregl.addProtocol(protocol,(request)=>{consturl=request.url.replace(protocol+'://','');returnfetch(url).then((response)=>response.arrayBuffer()).then((data)=>newVectorTile(newProtobuf(data))).then((tile)=>({layers:Object.entries(tile.layers).reduce((acc,[layerId,layer])=>({...acc,[layerId]:{...layer,feature:(index)=>{constfeature=layer.feature(index);if(feature.properties&&typeoffeature.properties['NAME']==='string'){feature.properties['NAME']=feature.properties['NAME'].split('').reverse().join('');}if(feature.properties&&typeoffeature.properties['ABBREV']==='string'){feature.properties['ABBREV']=feature.properties['ABBREV'].split('').reverse().join('');}returnfeature;}}}),{})})).then((tile)=>tileToProtobuf(tile).buffer).then((data)=>({data}));});constmap=newmaplibregl.Map({container:'map',style:'https://demotiles.maplibre.org/style.json',center:[8,47],zoom:5,hash:'map'});map.setTransformRequest((url,resourceType)=>{if(url.startsWith('https://demotiles.maplibre.org/tiles/')&&resourceType==='Tile'){return{url:protocol+'://'+url};}returnundefined;});

代码示例

<!DOCTYPEhtml><htmllang="en"><head><title>Use addProtocol to Transform Feature Properties</title><metaproperty="og:description"content="使用纯 JavaScript 的 addProtocol 反转国家名称。"/><metaproperty="og:created"content="2025-06-25"/><metacharset="utf-8"><metaname="viewport"content="width=device-width, initial-scale=1"><linkrel="stylesheet"href="https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.css"/><scriptsrc="https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.js"></script><style>body{margin:0;padding:0;}html, body, #map{height:100%;}</style></head><body><divid="map"></div><scripttype="module">importProtobuffrom'https://unpkg.com/pbf@4.0.1/index.js';import{VectorTile}from'https://esm.run/@mapbox/vector-tile@2.0.3/index.js';importtileToProtobuffrom'https://esm.run/vt-pbf@3.1.3/index.js';constprotocol='reverse';maplibregl.addProtocol(protocol,(request)=>{consturl=request.url.replace(protocol+'://','');returnfetch(url).then((response)=>response.arrayBuffer()).then((data)=>newVectorTile(newProtobuf(data))).then((tile)=>({layers:Object.entries(tile.layers).reduce((acc,[layerId,layer])=>({...acc,[layerId]:{...layer,feature:(index)=>{constfeature=layer.feature(index);if(feature.properties&&typeoffeature.properties['NAME']==='string'){feature.properties['NAME']=feature.properties['NAME'].split('').reverse().join('');}if(feature.properties&&typeoffeature.properties['ABBREV']==='string'){feature.properties['ABBREV']=feature.properties['ABBREV'].split('').reverse().join('');}returnfeature;}}}),{})})).then((tile)=>tileToProtobuf(tile).buffer).then((data)=>({data}));});constmap=newmaplibregl.Map({container:'map',style:'https://demotiles.maplibre.org/style.json',center:[8,47],zoom:5,hash:'map'});map.setTransformRequest((url,resourceType)=>{if(url.startsWith('https://demotiles.maplibre.org/tiles/')&&resourceType==='Tile'){return{url:protocol+'://'+url};}returnundefined;});</script></body></html>

🔍 代码解析

初始化地图

使用new maplibregl.Map()创建地图实例,配置基本参数。本示例的核心特色是展示如何使用maplibregl.addProtocol()方法创建自定义协议,在瓦片加载过程中转换要素属性。

constmap=newmaplibregl.Map({container:'map',style:'https://demotiles.maplibre.org/style.json',center:[8,47],// 欧洲中部zoom:5,hash:'map'});

关键配置项

  • container: 地图容器的 DOM 元素 ID
  • style: 使用 MapLibre 官方样式https://demotiles.maplibre.org/style.json
  • center: 地图初始中心点[8, 47](欧洲中部,德国附近)
  • zoom: 初始缩放级别为 5,显示中等区域范围
  • hash: 设置为'map',启用 URL hash 导航,便于分享地图状态

依赖导入

importProtobuffrom'https://unpkg.com/pbf@4.0.1/index.js';import{VectorTile}from'https://esm.run/@mapbox/vector-tile@2.0.3/index.js';importtileToProtobuffrom'https://esm.run/vt-pbf@3.1.3/index.js';

本示例使用三个关键库:

  • pbf: 用于解析 Protocol Buffers 格式数据
  • @mapbox/vector-tile: 用于解析矢量瓦片数据
  • vt-pbf: 用于将数据转换回 Protocol Buffers 格式

自定义协议实现

constprotocol='reverse';maplibregl.addProtocol(protocol,(request)=>{// 1. 提取原始 URL(移除自定义协议前缀)consturl=request.url.replace(protocol+'://','');returnfetch(url)// 2. 获取瓦片数据(ArrayBuffer 格式).then((response)=>response.arrayBuffer())// 3. 解析为 VectorTile 对象.then((data)=>newVectorTile(newProtobuf(data)))// 4. 转换要素属性(反转国家名称).then((tile)=>({layers:Object.entries(tile.layers).reduce((acc,[layerId,layer])=>({...acc,[layerId]:{...layer,feature:(index)=>{constfeature=layer.feature(index);// 反转 NAME 属性(国家名称)if(feature.properties&&typeoffeature.properties['NAME']==='string'){feature.properties['NAME']=feature.properties['NAME'].split('').reverse().join('');}// 反转 ABBREV 属性(国家缩写)if(feature.properties&&typeoffeature.properties['ABBREV']==='string'){feature.properties['ABBREV']=feature.properties['ABBREV'].split('').reverse().join('');}returnfeature;}}}),{})}))// 5. 转换回 Protocol Buffers 格式.then((tile)=>tileToProtobuf(tile).buffer)// 6. 返回处理后的数据.then((data)=>({data}));});

自定义协议工作流程

  1. 拦截带有reverse://前缀的请求
  2. 提取原始 URL 并发起网络请求
  3. 解析矢量瓦片数据
  4. 修改要素属性(本示例中反转国家名称)
  5. 将修改后的数据转换回原始格式
  6. 返回处理后的数据供地图渲染

请求转换配置

map.setTransformRequest((url,resourceType)=>{// 将特定 URL 转换为自定义协议if(url.startsWith('https://demotiles.maplibre.org/tiles/')&&resourceType==='Tile'){return{url:protocol+'://'+url};}returnundefined;});

作用:将符合条件的瓦片请求重写为使用自定义协议,使得这些请求被我们注册的处理器拦截和处理。

⚙️ 参数说明

地图初始化参数

参数类型必填默认值说明
containerstring-地图容器元素的 ID
stylestring/object-地图样式 URL 或内联样式对象
center[number, number][0, 0]初始中心点坐标,格式为[经度, 纬度]
zoomnumber0初始缩放级别,范围 0-22
hashstring/boolean-启用 URL hash 导航

addProtocol 参数

参数类型必填说明
protocolstring自定义协议名称(如 ‘reverse’),用于标识协议前缀
handlerfunction处理请求的回调函数,接收 request 对象并返回 Promise

handler 函数参数

参数类型说明
requestobject请求对象,包含urlheadersmethod等属性
request.urlstring请求的完整 URL(包含自定义协议前缀)

handler 函数返回值

属性类型说明
dataArrayBuffer处理后的瓦片数据,必须是 Protocol Buffers 格式

setTransformRequest 参数

参数类型说明
urlstring请求的原始 URL
resourceTypestring资源类型,如 ‘Tile’、‘Style’、‘Source’ 等

setTransformRequest 返回值

属性类型说明
urlstring修改后的 URL(添加自定义协议前缀)

🎨 效果说明

运行代码后,页面显示一个交互式地图,其中国家名称被反转显示:

  • 名称反转: 地图上的国家名称(如 “France”)显示为反转后的形式(如 “ecnarF”),国家缩写(如 “Fr”)也被反转(如 “rF”)
  • 协议拦截: 通过自定义协议reverse://拦截矢量瓦片请求
  • 属性转换: 在瓦片加载过程中实时修改要素属性,无需修改原始数据源
  • 交互功能: 支持鼠标拖拽、滚轮缩放、右键旋转等标准交互

地图默认显示欧洲中部区域(中心 [8, 47]),缩放级别为 5。用户可以看到所有国家标签都以反转形式显示,直观展示了自定义协议的强大功能。

技术实现效果

  1. 所有矢量瓦片请求都经过自定义协议处理器
  2. 处理器解析瓦片数据,修改要素属性后返回
  3. 地图渲染修改后的属性,显示反转的国家名称
  4. 整个过程对用户透明,用户看到的只是显示效果的变化

💡 常 见 问 题

Q1: 什么是自定义协议?
A:自定义协议允许开发者拦截地图的网络请求,在数据加载过程中进行处理或转换。通过maplibregl.addProtocol()注册协议后,可以处理特定协议前缀(如reverse://)的 URL。

Q2: 自定义协议的工作流程是什么?
A:

  1. 注册协议: 使用maplibregl.addProtocol()注册自定义协议和处理函数
  2. 转换请求: 使用map.setTransformRequest()将特定 URL 转换为自定义协议格式
  3. 拦截请求: 当地图请求资源时,匹配到自定义协议的请求会被拦截
  4. 处理数据: 处理器获取原始数据,进行转换处理(如修改属性)
  5. 返回结果: 返回处理后的数据供地图渲染

Q3: 自定义协议有哪些应用场景?
A:

  • 数据加密/解密: 在加载时解密加密的瓦片数据
  • 属性转换和处理: 修改要素属性(如本示例中的名称反转)
  • 数据格式转换: 转换不同的数据格式
  • 请求缓存和优化: 实现自定义缓存策略
  • 数据过滤和裁剪: 根据条件过滤或裁剪数据
  • 请求代理: 转发请求到不同的服务器

Q4: 如何调试自定义协议?
A:

  1. 在处理器函数中添加console.log()输出,查看请求和响应数据
  2. 使用浏览器开发者工具的 Network 面板查看请求
  3. 使用debugger语句设置断点
  4. 检查返回的数据格式是否正确

Q5: 自定义协议只适用于矢量瓦片吗?
A:不是。自定义协议可以用于任何类型的地图资源请求,包括矢量瓦片、栅格瓦片、样式文件、精灵图等。

Q6: 如何移除自定义协议?
A:使用maplibregl.removeProtocol(protocol)方法移除已注册的自定义协议:

maplibregl.removeProtocol('reverse');

📝 练习任务

  1. 基础练习:修改反转逻辑,改为将国家名称转换为大写(使用toUpperCase()方法)
  2. 进阶挑战:添加错误处理,当请求失败时返回默认数据或使用缓存数据
  3. 拓展练习:创建一个自定义协议,实现简单的请求缓存机制
  4. 拓展思考:如何实现基于自定义协议的数据缓存机制?需要考虑哪些因素(缓存策略、过期时间、内存管理等)?

🌟 最佳实践

  1. 协议命名: 使用有意义的协议名称,便于识别和维护(如secure://cached://
  2. 错误处理: 添加完善的错误处理机制,避免地图加载失败,使用catch捕获异常
  3. 性能优化: 对处理逻辑进行优化,避免阻塞主线程,考虑使用 Web Worker
  4. 数据验证: 在处理数据前验证数据格式和完整性,确保返回正确的格式
  5. 资源清理: 在页面卸载时使用removeProtocol()清理自定义协议
  6. 文档说明: 添加注释说明自定义协议的用途和工作原理
  7. 测试覆盖: 测试各种边界情况和异常输入
  8. 协议隔离: 不同功能使用不同的协议名称,避免冲突
  9. 请求过滤: 在setTransformRequest中精确匹配需要处理的 URL,避免不必要的拦截

🔗 延伸阅读

  • Map API文档

  • MapLibre GL JS 官方文档

  • [下一课预告]:将继续学习地图图层的基础知识


本文是MapLibre GL JS实践课程系列的一部分,欢迎关注收藏

http://www.jsqmd.com/news/918533/

相关文章:

  • Qoder使用二:内置智能体
  • 智谱AI完成5亿美元融资 + AutoGLM 2.0发布:对标GPT-5 Agent Mode
  • GTA5 村长团队Blender Sollumz 人物制作+权重绘制全流程超详细教程
  • Selenium自动化测试:除了放Scripts目录,ChromeDriver还有这3种灵活配置方法
  • 3PEAK思瑞浦 TP2191-TR SOT23-5 运算放大器
  • Hyperf 利用 PHP 的 反射机制的庖丁解牛
  • spi_master
  • 第八届高分子化学国际研讨会 (ICPC 2026)
  • Python类型推导协议
  • DeepSeek V1
  • [智能体-128]:智能体,模型与工具的整合者
  • OpenAI GPT-5 Agent Mode 正式发布:最长24小时自主任务,AI编程智能体大战升级
  • 城通网盘解析器:3分钟掌握免费高速下载的终极方案
  • TrafficMonitor插件完全指南:打造你的个性化桌面监控中心
  • OpencvSharp 算子学习教案之 - Cv2.CvtColor
  • MATLAB图论实战:除了shortestpath,自己写的Dijkstra函数如何优化与可视化?
  • 基于知识图谱与专家系统的散热材料智能推荐技术
  • 3PEAK思瑞浦 TP5551-TR SOT23-5 精密运放
  • OmenSuperHub:彻底释放惠普暗影精灵游戏本性能的终极解决方案
  • 智能体协同下的数字孪生IOC:端流融合与场景编排的工程选型逻辑
  • 双系统Ubuntu18.04升级22.04,安装docker进行openclaw安装
  • OpencvSharp 算子学习教案之 - Cv2.CvtColorTwoPlane
  • 如何高效解密网易云音乐NCM文件:ncmdumpGUI完整技术解析与实战指南
  • 避坑指南:在LabVIEW 2023中设计波形发生器UI时,如何优雅管理控件状态与数据流?
  • 【电赛保姆级教程】别在比赛时从零写代码了!电赛“祖传代码库”搭建与OLED多级菜单硬核指南
  • 用Java+SpringBoot给服务器告警邮件找个‘飞书管家’:保姆级配置教程(附避坑点)
  • Debian 11 Bullseye 新装后必做的 10 件事:从内核 5.10 到 LibreOffice 7.0 的实用调优
  • 量子计算中的测量基优化与误差缓解技术
  • 26年AI漫剧制作厂商排行榜多家深度格局解析 - 速递信息
  • 河北君宏泵业:排污泵/循环泵/隔膜泵/消防泵/混流泵专业制造与多场景应用 - 品牌推荐官