news 2026/5/30 20:44:33

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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实践课程系列的一部分,欢迎关注收藏

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/30 20:43:57

Java 生产环境高并发设计全方案

目录 一、整体高并发设计核心原则 二、接入层高并发设计&#xff08;第一道防线&#xff09; 1. 负载均衡&#xff08;水平扩容核心&#xff09; 2. 限流&#xff08;防止流量冲垮服务&#xff09; 3. 静态资源分离 三、应用层&#xff08;Java 服务&#xff09;高并发设…

作者头像 李华
网站建设 2026/5/30 20:39:17

第二类医疗器械:家用血糖仪及试纸模拟运输测试案列

第二类医疗器械&#xff1a;家用血糖仪及试纸模拟运输测试案列本次测试依据ASTM D4169-22标准&#xff0c;选择DC 4&#xff08;小包裹运输&#xff09;配送周期&#xff0c;模拟家用血糖仪套装从江苏工厂发往全国各省市终端消费者的运输场景。测试样品为血糖仪试纸x支采血针的…

作者头像 李华
网站建设 2026/5/30 20:37:10

Hitboxer终极指南:5分钟掌握游戏按键重映射的免费神器

Hitboxer终极指南&#xff1a;5分钟掌握游戏按键重映射的免费神器 【免费下载链接】socd Key remapper for epic gamers 项目地址: https://gitcode.com/gh_mirrors/so/socd 你是否在玩游戏时遇到过按键冲突的困扰&#xff1f;比如同时按下左右方向键时角色卡顿&#xf…

作者头像 李华
网站建设 2026/5/30 20:31:30

基于 SpringBoot + Redis (Lettuce) + RabbitMQ 实现「Redis 预扣库存 + 异步同步数据库」

一、整体流程回顾 前端下单请求进来,先查 Redis 库存 执行 DECR 原子扣减: 结果 < 0 → 库存不足,直接返回失败 结果 ≥ 0 → 扣减成功,发送消息到 MQ MQ 消费者消费消息,异步更新数据库库存 配套:消息重试、定时对账、DB 乐观锁防重复扣减 二、核心依赖(pom.xml 关键…

作者头像 李华