MapLibre GL JS第20课:更新GeoJSON多边形
学习目标掌握更新GeoJSON多边形的实现方法理解相关API的使用能够独立完成类似功能开发 核心概念使用可更新的GeoJSONVT更新GeoJSON多边形。 完 整 代 码代码示例constmapnewmaplibregl.Map({container:map,style:https://demotiles.maplibre.org/style.json,center:[-68.13734351262877,45.137451890638886],zoom:5});map.showTileBoundariestrue;constrectanglesArray.from({length:5},(_,i)({id:i,x:-68.13(Math.random()-0.5)*5,y:45.13(Math.random()-0.5)*5,vx:(Math.random()-0.5)*0.05,vy:(Math.random()-0.5)*0.05,w:0.5Math.random(),h:0.5Math.random(),rotation:Math.random()*2*Math.PI,rotationSpeed:(Math.random()-0.5)*0.1,color:#008888,every:Math.round(Math.random()*100200)}));constgetRandomColor()#Math.floor(Math.random()*16777215).toString(16).padStart(6,0);functiongetRectangleGeometry({x,y,w,h,rotation}){constcMath.cos(rotation),sMath.sin(rotation);consthww/2,hhh/2;constcoords[[-hw,-hh],[hw,-hh],[hw,hh],[-hw,hh]].map(([dx,dy])[xdx*c-dy*s,ydx*sdy*c]);coords.push(coords[0]);return{type:Polygon,coordinates:[coords]};}map.on(load,(){constfeaturesrectangles.flatMap(rect[{type:Feature,id:rect.id,properties:{color:rect.color},geometry:getRectangleGeometry(rect)},{type:Feature,id:${rect.id}_label,properties:{label:Zoom:${Math.round(map.getZoom())}},geometry:{type:Point,coordinates:[rect.x,rect.y]}}]);map.addSource(rectangles,{type:geojson,data:{type:FeatureCollection,features}});map.addLayer({id:rectangles,type:fill,source:rectangles,paint:{fill-color:[get,color],fill-opacity:0.8},filter:[,$type,Polygon]});map.addLayer({id:rectangles-label,type:symbol,source:rectangles,layout:{text-field:[get,label],text-size:14,text-allow-overlap:true,text-ignore-placement:true},paint:{text-color:#ffffff},filter:[,$type,Point]});letcount0;functionanimate(){constzoommap.getZoom().toFixed(1);count;constupdatesrectangles.flatMap(rect{rect.xrect.vx;rect.yrect.vy;rect.rotationrect.rotationSpeed;if(rect.x-75||rect.x-60)rect.vx*-1;if(rect.y40||rect.y50)rect.vy*-1;if(count%rect.every0)rect.colorgetRandomColor();return[{id:rect.id,newGeometry:getRectangleGeometry(rect),addOrUpdateProperties:[{key:color,value:rect.color}]},{id:${rect.id}_label,newGeometry:{type:Point,coordinates:[rect.x,rect.y]},addOrUpdateProperties:[{key:label,value:zoom}]}];});map.getSource(rectangles)?.updateData({update:updates});requestAnimationFrame(animate);}animate();});代码示例!DOCTYPEhtmlhtmllangenheadtitleUpdate GeoJSON polygons/titlemetapropertyog:descriptioncontent使用可更新的 GeoJSONVT 更新 GeoJSON 多边形/metapropertyog:createdcontent2026-03-01/metacharsetutf-8metanameviewportcontentwidthdevice-width, initial-scale1linkrelstylesheethrefhttps://unpkg.com/maplibre-gl5.24.0/dist/maplibre-gl.css/scriptsrchttps://unpkg.com/maplibre-gl5.24.0/dist/maplibre-gl.js/scriptstylebody{margin:0;padding:0;}html, body, #map{height:100%;}/style/headbodydividmap/divscriptconstmapnewmaplibregl.Map({container:map,style:https://demotiles.maplibre.org/style.json,center:[-68.13734351262877,45.137451890638886],zoom:5});map.showTileBoundariestrue;constrectanglesArray.from({length:5},(_,i)({id:i,x:-68.13(Math.random()-0.5)*5,y:45.13(Math.random()-0.5)*5,vx:(Math.random()-0.5)*0.05,vy:(Math.random()-0.5)*0.05,w:0.5Math.random(),h:0.5Math.random(),rotation:Math.random()*2*Math.PI,rotationSpeed:(Math.random()-0.5)*0.1,color:#008888,every:Math.round(Math.random()*100200)}));constgetRandomColor()#Math.floor(Math.random()*16777215).toString(16).padStart(6,0);functiongetRectangleGeometry({x,y,w,h,rotation}){constcMath.cos(rotation),sMath.sin(rotation);consthww/2,hhh/2;constcoords[[-hw,-hh],[hw,-hh],[hw,hh],[-hw,hh]].map(([dx,dy])[xdx*c-dy*s,ydx*sdy*c]);coords.push(coords[0]);return{type:Polygon,coordinates:[coords]};}map.on(load,(){constfeaturesrectangles.flatMap(rect[{type:Feature,id:rect.id,properties:{color:rect.color},geometry:getRectangleGeometry(rect)},{type:Feature,id:${rect.id}_label,properties:{label:Zoom:${Math.round(map.getZoom())}},geometry:{type:Point,coordinates:[rect.x,rect.y]}}]);map.addSource(rectangles,{type:geojson,data:{type:FeatureCollection,features}});map.addLayer({id:rectangles,type:fill,source:rectangles,paint:{fill-color:[get,color],fill-opacity:0.8},filter:[,$type,Polygon]});map.addLayer({id:rectangles-label,type:symbol,source:rectangles,layout:{text-field:[get,label],text-size:14,text-allow-overlap:true,text-ignore-placement:true},paint:{text-color:#ffffff},filter:[,$type,Point]});letcount0;functionanimate(){constzoommap.getZoom().toFixed(1);count;constupdatesrectangles.flatMap(rect{rect.xrect.vx;rect.yrect.vy;rect.rotationrect.rotationSpeed;if(rect.x-75||rect.x-60)rect.vx*-1;if(rect.y40||rect.y50)rect.vy*-1;if(count%rect.every0)rect.colorgetRandomColor();return[{id:rect.id,newGeometry:getRectangleGeometry(rect),addOrUpdateProperties:[{key:color,value:rect.color}]},{id:${rect.id}_label,newGeometry:{type:Point,coordinates:[rect.x,rect.y]},addOrUpdateProperties:[{key:label,value:zoom}]}];});map.getSource(rectangles)?.updateData({update:updates});requestAnimationFrame(animate);}animate();});/script/body/html 代码解析1. 初始化地图使用new maplibregl.Map()创建地图实例配置基本参数。启用showTileBoundaries显示瓦片边界。2. 创建矩形数据生成5个随机矩形包含位置、速度、尺寸、旋转角度等属性。3. 几何计算函数getRectangleGeometry()函数根据矩形参数计算旋转后的坐标使用三角函数进行旋转变换。4. 添加数据源和图层创建GeoJSON数据源包含多边形和标签点要素添加fill和symbol图层。5. 动画循环使用requestAnimationFrame实现平滑动画通过updateData()方法高效更新要素。6. 边界检测矩形到达边界时反弹反转速度方向并随机改变颜色。⚙️ 参数说明参数类型必填说明containerstring是地图容器IDstylestring是地图样式URLcenter[number, number]否初始中心点默认[0, 0]zoomnumber否初始缩放级别默认0updateData 方法参数参数类型说明updatearray要素更新数组update[].idstring/number要素IDupdate[].newGeometryobject新的几何对象update[].addOrUpdatePropertiesarray属性更新数组 效果说明运行代码后地图显示美国缅因州区域中心点 -68.14°W, 45.14°N5个半透明矩形在地图上移动、旋转矩形到达边界时自动反弹矩形颜色随机变化每个矩形中心显示当前缩放级别标签瓦片边界可见便于调试 常 见 问 题Q1: updateData 和 setData 有什么区别A:updateData是增量更新只更新变化的要素setData是全量替换。Q2: 如何暂停/恢复动画A:可以使用cancelAnimationFrame()暂停重新调用animate()恢复。Q3: 旋转矩形的几何计算原理是什么A:使用旋转变换矩阵x x*cosθ - y*sinθ, y x*sinθ y*cosθ 练习任务基础练习修改矩形数量和初始颜色进阶挑战添加暂停/播放按钮控制动画拓展思考如何实现矩形碰撞检测 最佳实践增量更新: 使用updateData替代setData提升性能动画优化: 使用requestAnimationFrame确保流畅的60fps动画内存管理: 避免在动画循环中创建新对象边界处理: 合理设置边界条件避免要素移出可视区域 延伸阅读Map API文档MapLibre GL JS 官方文档[下一课预告]将继续学习地图图层的基础知识本文是MapLibre GL JS实践课程系列的一部分欢迎关注收藏