当前位置: 首页 > 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.rkmt.cn/news/1429851.html

相关文章:

  • GTA5 村长团队Blender Sollumz 人物制作+权重绘制全流程超详细教程
  • OpenAI GPT-5 Agent Mode 正式发布:最长24小时自主任务,AI编程智能体大战升级
  • TrafficMonitor插件完全指南:打造你的个性化桌面监控中心
  • 基于知识图谱与专家系统的散热材料智能推荐技术
  • 智能体协同下的数字孪生IOC:端流融合与场景编排的工程选型逻辑
  • 避坑指南:在LabVIEW 2023中设计波形发生器UI时,如何优雅管理控件状态与数据流?
  • 如何高效解密网易云音乐NCM文件:ncmdumpGUI完整技术解析与实战指南
  • 26年AI漫剧制作厂商排行榜多家深度格局解析 - 速递信息
  • 量子计算中的测量基优化与误差缓解技术
  • Mac重装系统卡在“最后1秒”?别慌,这可能是APFS格式和安装时间预估的锅
  • 新 E 选品牌源头厂家无溶剂 PU 烤火罩耐刮耐磨吗
  • bugkuctf-web-文件上传(kali操作)
  • 实习19-HRM
  • windows 常见的cmd备忘录
  • 从Remy到3D空间影像壁纸,鸿蒙3DGS的差异性体验,凭什么得到消费者的认可?
  • 「斯隆奖」得主戴亮全职加盟复旦
  • 终极键盘连击修复指南:Keyboard Chatter Blocker 专业防抖工具完全教程
  • 【多模态评测】图生文/文生图模型怎么测?多模态评测集 CLIP Score 介绍
  • AI工具学习不是学软件,而是重构工作流:1套可迁移能力框架+5类岗位适配方案
  • Linux权限进阶:从passwd命令到SUID/SGID,搞懂那些‘s’和‘t’到底怎么用
  • 如何告别图片格式烦恼?Save Image as Type让网页图片一键转换
  • 如何快速修改暗黑破坏神2存档:5分钟掌握d2s-editor完整使用指南
  • FanControl深度解析:Windows风扇控制从入门到精通的完整指南
  • Python 潮流周刊#152:编程智能体终于跨过质量门槛了?
  • 网盘直链下载助手:告别限速,解锁九大网盘高速下载方案
  • 为什么你的芯片离不开它?聊聊带隙基准在LDO、ADC里的那些事儿
  • Liquid AI 发布 LFM2.5-8B-A1B:38T 训练的 8B 稀疏 MoE 模型,128K 上下文,笔记本就能跑
  • 闽侯经济纠纷律师评测推荐:从胜诉实绩看服务靠谱度 - 速递信息
  • AI技能化落地:从对话式大模型到可生产、可复用的AI工程体系
  • MTKClient核心技术深度解析:联发科设备底层调试与修复实战指南