如何在Python代码中通过JS Leaflet扩展Folium功能(如测距)
没问题!既然Folium是Leaflet的Python封装,那咱们完全可以直接调用Leaflet的原生API来实现Folium没提供的功能,不管是计算坐标距离还是删除标记都可以搞定。下面给你具体的实现步骤和代码示例:
一、调用Leaflet.LatLng.distanceTo计算坐标距离
Leaflet的distanceTo方法可以直接计算两个经纬度点之间的球面距离(底层用的就是球面余弦定理),咱们只需要在Folium生成的HTML中插入自定义JavaScript代码,就能调用这个方法。
具体实现步骤:
- 先正常用Folium创建地图和标记;
- 编写自定义JS代码,获取Leaflet的地图实例和标记对象,调用
distanceTo计算距离; - 将这段JS代码插入到Folium生成的HTML中,再加载到QtWebEngineView里。
示例代码:
import folium import io from PyQt5 import QtWebEngineWidgets # 假设你的起止坐标 lat_1, lng_1 = 32.5236, 54.0 lat_2, lng_2 = 33.0, 55.0 # 创建Folium地图 self.m = folium.Map( location=[32.5236, 54], tiles="Stamen terrain", zoom_start=5, control_scale=True, box_zoom=True ) self.marker1 = folium.Marker((lat_1, lng_1)).add_to(self.m) self.marker2 = folium.Marker((lat_2, lng_2)).add_to(self.m) self.m.add_child(folium.LatLngPopup()) # 自定义JavaScript代码:调用Leaflet的distanceTo计算距离 custom_js = f""" <script> // 获取Leaflet地图实例(Folium会把地图对象挂载到window.map上) const map = window.map; // 获取所有标记对象(Folium生成的标记存在map._markers里) const allMarkers = Object.values(map._markers); const marker1 = allMarkers[0]; const marker2 = allMarkers[1]; // 计算两点距离,单位是米 const distance = marker1.getLatLng().distanceTo(marker2.getLatLng()); console.log('起止坐标距离:', Math.round(distance) + ' 米'); // 可选:在地图中间弹出距离信息 const distancePopup = L.popup() .setLatLng([{(lat_1 + lat_2)/2}, {(lng_1 + lng_2)/2}]) .setContent(`<strong>两点距离</strong><br>${Math.round(distance)} 米`) .openOn(map); </script> """ # 保存地图并插入自定义JS self.data = io.BytesIO() self.m.save(self.data, close_file=False) html_content = self.data.getvalue().decode() # 在</body>标签前插入JS,确保Leaflet已经加载完成 html_content = html_content.replace('</body>', custom_js + '</body>') # 加载到QtWebEngineView self.map = QtWebEngineWidgets.QWebEngineView(self.centralwidget) self.map.setHtml(html_content)
二、借助Leaflet实现标记删除功能
Folium本身删除标记确实不太方便,但咱们可以用Leaflet的removeLayer方法来删除标记,同样通过自定义JS或者Python端调用JS来实现。
方法1:在地图上添加删除按钮(前端交互)
把这段JS加到上面的custom_js里,就能给地图加一个删除所有标记的按钮:
// 添加删除按钮控件 const deleteControl = L.control({position: 'topright'}); deleteControl.onAdd = function(map) { const btnDiv = L.DomUtil.create('div', 'leaflet-bar leaflet-control'); btnDiv.innerHTML = '<button style="padding: 6px 8px; border: none; background: white; cursor: pointer;">删除所有标记</button>'; btnDiv.onclick = function() { // 遍历所有标记并删除 Object.values(map._markers).forEach(marker => { map.removeLayer(marker); }); }; return btnDiv; }; deleteControl.addTo(map);
方法2:从Python端触发删除(Qt交互)
如果需要在Python代码里(比如用户点击GUI按钮时)删除标记,可以用QtWebEngine的runJavaScript方法执行删除逻辑:
# 假设这是你的GUI按钮点击事件处理函数 def on_delete_markers_clicked(self): self.map.page().runJavaScript(""" const map = window.map; Object.values(map._markers).forEach(marker => { map.removeLayer(marker); }); """)
思路总结
Folium生成的HTML本质就是一个Leaflet网页,所有Leaflet的原生API都能通过JavaScript调用。咱们的核心思路就是:
- 要么在Folium保存地图后,修改HTML内容插入自定义JS;
- 要么通过QtWebEngine的
runJavaScript方法动态执行JS代码,实现和Leaflet地图的交互。
内容的提问来源于stack exchange,提问作者Ash
相关产品推荐
相关产品推荐

