一种基于Leaflet.Legend的图例动态更新方法
目录
前言
一、场景再现
1、需求描述
2、核心方法介绍
3、存在的问题
二、问题解决
1、重复解决办法
2、图例不展示解决办法
3、成果展示
三、总结
前言
在当今数字化时代,地理信息系统(GIS)技术已经广泛应用于各个领域,从城市规划到环境监测,从交通管理到资源勘探等。而地图可视化作为 GIS 的重要组成部分,为用户提供了直观、便捷的方式来展示和分析地理空间数据。Leaflet 作为一种轻量级、开源的地图库,因其简单易用、高度可定制化以及良好的兼容性,受到了众多开发者的青睐,被广泛应用于各种地图应用的开发中。而在地图可视化中,图例的作用不可忽视。它为用户提供了地图上各种符号、颜色和图层所代表的含义,帮助用户更好地理解和解读地图信息。
关于如何在Leaflet中进行图例生成,原博文介绍:LeafLet加载自定义Legend的设计与实现。在后面的项目当中基本都是一次性生成图例,然后在地图上展示。然而,在最近的实际开发过程中,遇到了一个棘手的问题:Leaflet 的图例无法动态更新。当地图上的数据发生变化,例如添加或移除图层、修改图层样式、更新数据源等时,图例却无法及时反映这些变化,这给用户带来了极大的不便,也影响了地图应用的用户体验和实用性。因此需要程序能根据变化或者事件响应动态更新。在实际实践中我们发现,leaflet.legend的图例动态更新存在一定的局限性。它通常是基于初始加载时的图层和样式信息来生成图例的,而当这些信息发生变化时,图例并不会自动重新生成或更新。这使得开发者在面对动态数据和交互式地图时,难以实现图例与地图的同步更新。
本篇文章将深入探讨解决 Leaflet.Legend 图例无法动态更新的一种方法。我们将从问题出发,分析现有解决方案的不足之处,然后详细介绍一种基于事件监听和动态渲染机制的方法。通过这种方法,我们可以在地图数据发生变化时,自动检测并动态更新图例,确保图例始终与地图上的数据保持一致。我们将通过实际代码示例和测试结果,展示该方法的有效性和可行性,并探讨其在不同应用场景中的适用性和优化方向。
通过本文的介绍,希望能够为广大的 Leaflet 开发者提供一种全新的思路和实用的解决方案,帮助他们在地图开发过程中更好地应对图例动态更新的挑战,提升地图应用的质量和用户体验。同时,也期待与更多开发者交流和分享经验,共同推动 Leaflet 技术的发展和创新。
一、场景再现
本节将从需求场景、核心方法介绍、存在的问题三个方面对开发过程遇到的问题进行讲解。通过本节的阐述,可以让朋友们对leaflet.legend的核心方法有更详细的认识,通过结合核心API方法和存在的问题,为下一步问题的解决奠定方向和基础。
1、需求描述
在以往的一些WebGIS应用展示中,比如展示地震的震级、风景区的等级等,这些图例信息往往都是固定的,我们可以将地震震级分成不同的震级,比如3级以下,3-5级,5-7级,7级以上等。又比如在风景区的等级中中的5A、4A、3A、2A、1A等,如下图。
这些图例在地图进行展示时基本上已经是固定的,后续需要动态变化的只是调整数据的输出,而图例基本上是不会改变的。暂且我们将这些图例成为静态图例。与这种需求不一样的是,如果在应用中,我们需要动态切换数据图层,比如从行政区划图层切换到土地利用图层,图例不仅内容也要发生变化,即维度的变化。同时在渲染的样式、具体的图例文字方面都要进行变化,内容上从行政区划名称切换成不同的土地利用类型。还有一些场景是跟随时间维度变化的,随着时间的推移,图例会新增或者移除部分老的图例。比如在在风景区分级中,需要将2A和3A级景区进行合并。从而减少图例的展示个数。这样的场景非常多,不管是基于事件驱动还是时间驱动,都会对关联的图例的有所影响。以上就是本文遇到的几个需求场景,这里只描述了其中的一两种,现实当中肯定有更多的场景,就不注意列举了。
2、核心方法介绍
与后端的OOP编程思想相类似,前端也是采用类似OOP的思想进行开发。因此对相关的对象的属性和方法进行封装。关于leaflet.legend的属性,这里不再进行赘述,但是对核心的API方法还是需要进行详细叙述。因为在进行图例内容的动态生成中,肯定需要调用leaflet.legend提供的方法来实现图例的动态生成的。这里将详细介绍其相关的方法。
除了对象的Options配置参数外,以上就是比较重要的几个API方法。 下面将对图例对象的初始化、图例容器构建、图例绘制、事件响应、重绘等几个方法进行介绍。帮助大家对组件有更深入的理解。我们来看一下在原来的代码中是如何将图例添加到地图中的,原始方法代码如下:
function updateLegend(legendData){var legend = L.control.Legend({position: "bottomright",collapsed: false,symbolWidth: 24,opacity: 1,title:"图例",column: 2,legends: legendData});legend.addTo(map);
}
在Javascript中进行代码断点来看一下将legend对象添加到地图后,会发生什么? 首先将调用的是initialize()方法,可以在参数入口处看到传入的参数。
在将图例符号进行清空后,将进行图例容器对象的创建,调用的_buildContainer()方法,以此来绑定和展示图例信息。
请注意,在这里创建用于图例展示的div容器(这个方法很重要,请注意这个方法,在这个方法里,会创建容器,在后面的问题解决过程中会涉及重复对象的创建,因此在调整时需要规避这个问题), 在这个方法中,通过循环配置参数中的不同图例信息,循环构建图例信息。构建图例元素的方法是:_buildLegendItems();
通过以上对核心方法的介绍,相信大家对于组件的相关API有了进一步的认识。在后面的问题解决阶段还会对这些方法有所涉及。
3、存在的问题
在最开始开发的时候,往往由于没有深入的研究相关的API,极容易出现一些错误。比如图例越来越多,在地图上的位置都展示不了了。又或者是事件触发后,相应的图例并没有生成,没有任何效果,当前图例没有生成,或者是产生了报错。下面我们对这几种情况分别进行说明,如最开始的代码所示,第一种情况就是图例越来越多。
function updateLegend(legendData){var legend = L.control.Legend({position: "bottomright",collapsed: false,symbolWidth: 24,opacity: 1,title:"图例",column: 2,legends: legendData});legend.addTo(map);
}
程序运行后,可以在图例的展示栏中看到图例越来越多,虽然新的产生了,但老的没有替换到。如下图所示:
第二种情况是开发的同学稍微了解组件的方法,在获得legend对象后,重新对图例数据进行赋值,然后调用重绘的方法,代码如下:
function updateLegend(legendData){if(undefined == legend){legend = L.control.Legend({position: "bottomright",collapsed: false,symbolWidth: 24,opacity: 1,title:"图例",column: 2,legends: legendData});}else{//step1、重新设置数据legend.options.legends = legendData;//step2、重新绘制legend.redraw();}legend.addTo(map);
}
咋一看起来,似乎是非常好的解决办法,但是实际运行后却是以下的情况:
不仅没有出现新的图例,老的图例都消失了。 在查看浏览器的控制台时,可以发现以下的报错:
更详细的是以下的代码行中出现问题,在这个重绘的方法中,其调用的是构建单个图例的重绘。在方法调用时需要传入容器对象和当前的图例对象。在redraw方法中,传入了空的值。类似于报了空指针异常。
以上就是在开发当中极易碰到的问题,这里先讲讲遇到的两个问题场景,如果您开发当中也遇到这种问题,不妨来这里看看。
二、问题解决
本节将重点围绕第一节中的遇到的问题,重点对发现或者产生的问题进行解决。主要是围绕图例的重复生成、图例的正确重绘方法进行介绍,在介绍的过程中又会回顾到前面的API核心接口讲解。因此这是一个动手的过程,希望你可以跟着一起来动手实践,纸上得来终觉浅,绝知此事要躬行。只有经过自己的亲自动手,才能彻底得掌握如何应对和解决。
为了模拟图例的动态生成,在Leaflet页面上,新增了一个“图例更新”的按钮,用这个按钮的模拟事件来进行模拟需要动态切换的场景。模拟的按钮及其相关处理事件如下:
<div class="center-flex"><input type="button" class="opt_button" value="更新图例" onclick="updateLegendEvent()"></input>
</div>
其处理的回调方法如下,请注意在程序中设置一个标志位,用于切换不同的图例数据对象,在真实情况中,可以通过ajax来请求服务的接口后获得:
function updateLegendEvent(){var updateLegendData;if(sign == 0 ){updateLegendData = [{label: "位置",type: "image",url: "marker/marker-red.png"}, {label: "Marker2",type: "image",url: "marker/purple.png"}];sign = 1;} else {updateLegendData = initLegendData;sign = 0;}updateLegend(updateLegendData);
}
在有了事件的驱动之后,接下来就是图例对象的定义和展示。下面将深入如何避免在上一节中遇到的问题,针对性的提出解决办法。
1、重复解决办法
为了解决在图例生成时的重复问题,我们可以在进行图例对象生成时只生成一份,后续则在之前的对象中调用方法来修改相应的参数,以此来达到相应的目的。这个代码比较简单,在前面的方法中提到过,在初始化时增加一个判断,如果为空就创建对象,否则使用原来的对象:
function updateLegend(legendData){if(undefined == legend){legend = L.control.Legend({position: "bottomright",collapsed: false,symbolWidth: 24,opacity: 1,title:"图例",column: 2,legends: legendData});}else{//step1、重新设置数据legend.options.legends = legendData;//step2、重新绘制legend.redraw();}legend.addTo(map);
}
通过以上的代码就能避免图例对象重复创建的问题。
2、图例不展示解决办法
在解决图例的重复问题之后,又迎来一个新的难题,即图例在切换后不展示了。这又是什么问题呢?其实答案在第一节中的第三小节中已经有说明。在上面的代码中,我们调用的是redraw的方法,而这个方法最终调用的是_buildLegendItems: function (legendContainer, legend) {}方法,在这个方法中,需要传入一个图例容器和一个图例对象,而在redraw方法中并没有涉及这两个对象。因此才报的错。那么如何避免这种问题呢?解铃还须系铃人,要想解决图例不展示的问题,还得找到核心的问题,在前面的核心方法中,我们知道一套完备的可视化展示流程。即initialize方法调用_buildContainer方法,同时在_buildContainer方法中又会循环调用_buildLegendItems。通过这种方法,就会传入容器和图例对象,按照这种调用思路,在设置新的数据后,程序需要做的就是重新初始化,这样就会清空当前的图例对象,同时生成最新的图例。代码如下:
function updateLegend(legendData){if(undefined == legend){legend = L.control.Legend({position: "bottomright",collapsed: false,symbolWidth: 24,opacity: 1,title:"图例",column: 2,legends: legendData});}else{//动态加载图例的实现方式//step1、重新设置数据legend.options.legends = legendData;//step2、重新初始化(重新绘制)legend.initialize();}legend.addTo(map);
}
将代码进行改造后,图例可以正常展示了,但是重复的问题又产生了。如下图:
难道这种问题没有解决办法了么?仔细再想想,在初始化的方法中,依然还会创建容器对象出来,来看看它的源代码:
可以看到这个图例容器依然会重新创建,因此还是会产生重复。最终的答案是在每次初始化前将容器移除掉。代码很简单,如下:
function updateLegend(legendData){if(undefined == legend){legend = L.control.Legend({position: "bottomright",collapsed: false,symbolWidth: 24,opacity: 1,title:"图例",column: 2,legends: legendData});}else{//动态加载图例的实现方式//step1、重新设置数据legend.options.legends = legendData;//step2、销毁图例容器,保证不重复legend.getContainer().remove();//step3、重新初始化(重新绘制)legend.initialize();}legend.addTo(map);
}
3、成果展示
经过上述的改造,就能完美的解决图例重复已经切换后不展示的问题,下面来看一下最终的成果,点击“图例更新”按钮后,图例可以进行不停的切换,基本实现我们的需求,图例不会重复生成,也不会不见,控制台也不会报错,最终效果如下图所示:
三、总结
以上就是本文的主要内容,本篇文章将深入探讨解决 Leaflet.Legend 图例无法动态更新的一种方法。我们将从问题出发,分析现有解决方案的不足之处,然后详细介绍一种基于事件监听和动态渲染机制的方法。通过这种方法,我们可以在地图数据发生变化时,自动检测并动态更新图例,确保图例始终与地图上的数据保持一致。我们将通过实际代码示例和测试结果,展示该方法的有效性和可行性,并探讨其在不同应用场景中的适用性和优化方向。
通过本文的介绍,希望能够为广大的 Leaflet 开发者提供一种全新的思路和实用的解决方案,帮助他们在地图开发过程中更好地应对图例动态更新的挑战,提升地图应用的质量和用户体验。同时,也期待与更多开发者交流和分享经验,共同推动 Leaflet 技术的发展和创新。行文仓促,定有许多不足之处,如有不足在此恳请各位专家朋友批评指正,不胜感激。
相关文章:

一种基于Leaflet.Legend的图例动态更新方法
目录 前言 一、场景再现 1、需求描述 2、核心方法介绍 3、存在的问题 二、问题解决 1、重复解决办法 2、图例不展示解决办法 3、成果展示 三、总结 前言 在当今数字化时代,地理信息系统(GIS)技术已经广泛应用于各个领域,…...
Spring Boot: 使用 @Transactional 和 TransactionSynchronization 在事务提交后发送消息到 MQ
Spring Boot: 使用 Transactional 和 TransactionSynchronization 在事务提交后发送消息到 MQ 在微服务架构中,确保消息的可靠性和一致性非常重要,尤其是在涉及到分布式事务的场景中。本文将演示如何使用 Spring Boot 的事务机制和 TransactionSynchron…...
LQB(2)-python-枚举
前言 python中的枚举一般有两个说法,一个是枚举算法(暴力求解法,算法层面),一个是遍历使用enumerate()函数或者enum模块创建()。 暴力求解法在之前的博文里面讲过了👇,…...

MongoDB开发规范
分级名称定义P0核心系统需7*24不间断运行,一旦发生不可用,会直接影响核心业务的连续性,或影响公司名誉、品牌、集团战略、营销计划等,可能会造成P0-P2级事故发生。P1次核心系统这些系统降级或不可用,会间接影响用户使用…...
为什么DeepSeek服务器繁忙?
致敬DeepSeek 用户层面 用户数量激增:DeepSeek 免费且功能强大,对普通用户和开发者都极具吸引力124。尤其是在新功能推出、新模型上线或相关热门活动期间,大量用户会在短时间内涌入9。例如春节期间,DeepSeek 的用户量达到四千万7。…...

律所录音证据归集工具:基于PyQt6与多线程的自动化音频管理解决方案
在律所日常工作中,音频证据的整理与归集是一个高频且复杂的任务。面对大量的案件录音文件,如何实现快速且准确的分类与存档,成为了律所提高效率、降低出错率的关键。本文将通过技术角度解析一款名为律所录音证据归集工具的项目,详…...

【含开题报告+文档+PPT+源码】基于SpringBoot+Vue旅游管理网站
开题报告 本论文探讨了一款采用现代Web开发技术构建的台州市旅游综合信息与服务平台的设计与实现。该系统基于SpringBoot框架,以其轻量级、快速开发和强大的企业级应用支持能力为核心后端技术支撑,结合Vue.js前端框架及ElementUI组件库,为用…...

unity碰撞的监测和监听
1.创建一个地面 2.去资源商店下载一个火焰素材 3.把procedural fire导入到自己的项目包管理器中 4.给magic fire 0 挂在碰撞组件Rigidbody , Sphere Collider 5.创建脚本test 并挂在magic fire 0 脚本代码 using System.Collections; using System.Collections.Generic; usi…...

DeepSeek-R1 32B Windows+docker本地部署
最近国产大模型DeepSeek兴起,本地部署了一套deepseek同时集成Open WebUI界面,给大家出一期教程。 软件:Ollama、docker、Open WebUI 一、用Ollama下载模型 首先我们需要安装Ollama,它可以在本地运行和管理大模型。 到Ollama官网 https://ol…...
C++11新特性之unique_ptr智能指针
本节继续介绍智能指针,不了解的读者可以先阅读——C11新特性之shared_ptr智能指针-CSDN博客 1.介绍 unique_ptr是C11标准提供的另一种智能指针。与shared_ptr不同的是,unique_ptr指针指向的堆内存无法同其他unique_ptr共享,也就是每一片堆内…...

Vue与Konva:解锁Canvas绘图的无限可能
前言 在现代Web开发中,动态、交互式的图形界面已成为提升用户体验的关键要素。Vue.js,作为一款轻量级且高效的前端框架,凭借其响应式数据绑定和组件化开发模式,赢得了众多开发者的青睐。而当Vue.js邂逅Konva.js,两者结…...
python绘图之柱状堆积图的绘制
本节来学习用python来绘制柱状堆积图. 使用的库为matplotlib.pyplot,numpy 代码如下 # 导入必要的库 import matplotlib.pyplot as plt # 用于绘图 import numpy as np # 用于数值计算# 模拟一些数据 x [数值{}.format(i) for i in range(10)] # 创建一个包含10个元素的列…...

剪辑学习整理
文章目录 1. 剪辑介绍 1. 剪辑介绍 剪辑可以干什么?剪辑分为哪些种类? https://www.bilibili.com/video/BV15r421p7aF/?spm_id_from333.337.search-card.all.click&vd_source5534adbd427e3b01c725714cd93961af 学完剪辑之后如何找工作or兼职&#…...

DeepSeek从入门到精通:全面掌握AI大模型的核心能力
文章目录 一、DeepSeek是什么?性能对齐OpenAI-o1正式版 二、Deepseek可以做什么?能力图谱文本生成自然语言理解与分析编程与代码相关常规绘图 三、如何使用DeepSeek?四、DeepSeek从入门到精通推理模型推理大模型非推理大模型 快思慢想&#x…...
AI大模型训练实战:分布式与微调指南
AI大模型训练实战:分布式与微调指南 适用人群:有一定深度学习基础,正在或即将参与大模型(如 GPT、DeepSeek 等)训练与部署的工程师、研究者;想要理解分布式策略与微调方法的读者。 一、大模型为何需要分布式与微调? 随着 GPT、DeepSeek 等大模型参数规模攀升至数十亿甚…...
整合 Redis 分布式锁:从数据结构到缓存问题解决方案
引言 在现代分布式系统中,Redis 作为高性能的键值存储系统,广泛应用于缓存、消息队列、实时计数器等多种场景。然而,在高并发和分布式环境下,如何有效地管理和控制资源访问成为一个关键问题。Redis 分布式锁正是为了解决这一问题…...

并查集题目
并查集题目 聚合一块(蓝桥)合根植物(蓝桥)等式方程的可满足性省份数量 并查集(Union-Find)算法是一个专门针对「动态连通性」的算法。双方向的连通。 模板: class UF {// 连通分量个数private …...
日志2025.2.9
日志2025.2.9 1.增加了敌人挥砍类型 2.增加了敌人的死亡状态 在敌人身上添加Ragdoll,死后激活布偶模式 public class EnemyRagdoll : MonoBehaviour { private Rigidbody[] rigidbodies; private Collider[] colliders; private void Awake() { rigidbodi…...

支持多种网络数据库格式的自动化转换工具——VisualXML
一、VisualXML软件介绍 对于DBC、ARXML……文件的编辑、修改等繁琐操作,WINDHILL风丘科技开发的总线设计工具——VisualXML,可轻松解决这一问题,提升工作效率。 VisualXML是一个强大且基于Excel表格生成多种网络数据库文件的转换工具&#…...

Java并发编程笔记
Java并发基础知识补全 启动 启动线程的方式只有: 1、X extends Thread;,然后X.start 2、X implements Runnable;然后交给Thread运行 线程的状态 Java中线程的状态分为6种: 1. 初始(NEW):新创建了一个线程对象&…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...