I’m developing an application with PySide6 that visualize a GeoJSON over OSM, using Folium. Basically, I select a geojson from file, it shows in the map (into a pyqt window), and I should be able to select features in the geojson and do something with them (ie dynamically color some features if they are in a set that can vary at run time). This is the code of the widget for visualising the map.
import json
import os
import folium
from PySide6.QtCore import Slot, QObject, Signal
from PySide6.QtWebEngineCore import QWebEnginePage
from PySide6.QtWebEngineWidgets import QWebEngineView
from PySide6.QtWidgets import QVBoxLayout, QWidget, QSizePolicy
from branca.element import Element, JavascriptLink
class WebEnginePage(QWebEnginePage):
net_handler = None
on_map_clicked = Signal(float, float, int)
def __init__(self, parent):
super(WebEnginePage, self).__init__(parent)
self.parent = parent
def javaScriptConsoleMessage(self, level, msg, line, sourceID):
print(msg) # Check js errors
if 'lat' in msg:
dd = json.loads(msg)
self.net_handler.get_edge_id(dd['lng'], dd['lat'], dd['zoom'])
self.on_map_clicked.emit(dd['lat'], dd['lng'], dd['zoom'])
class FoliumDisplay(QWidget):
geojson_path = ""
def redraw_folium_map(self, ):
self.folium_map = folium.Map(zoom_start=self.zoom_level, location=(self.lon, self.lat),
tiles='cartodbdark_matter', min_zoom=14)
self.folium_map = self.add_geojson(self.folium_map, self.geojson_path)
self.webView.page().profile().clearHttpCache()
# save map data to data object
data = io.BytesIO()
self.folium_map.save(data, close_file=False)
self.webView.setHtml(data.getvalue().decode())
return data
def __init__(self, geojson_path, net_path):
super().__init__()
self.folium_map = None
self.lon = 50.83421264776447
self.lat = 4.366035461425782
self.zoom_level = 15
self.closed_edges = set()
self.geojson_path = geojson_path
self.window_width, self.window_height = 2000, 2000
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
layout = QVBoxLayout()
self.setLayout(layout)
self.webView = QWebEngineView() # start web engine
page = WebEnginePage(self)
page.on_map_clicked.connect(self.map_clicked)
self.webView.setPage(page)
self.redraw_folium_map()
layout.addWidget(self.webView)
@Slot(float, float, int)
def map_clicked(self, lon, lat, zoom_level):
self.lon = lon
self.lat = lat
self.zoom_level = zoom_level
def get_edge_color(self, feature):
edge_id = feature['properties']['id']
if edge_id not in self.closed_edges:
return "#1a73e8"
return "#D3D3D3"
def add_geojson(self, map, fname):
with open(fname, encoding='utf-8') as f:
data = json.load(f)
popup = folium.GeoJsonPopup(fields=["id", "name"])
elem = folium.GeoJson(data,
highlight_function=lambda feature: {"color": "#00FFF7"},
tooltip=folium.features.GeoJsonTooltip(
fields=['id', 'name'],
aliases=['Edge ID:', 'Name:'],
),
popup=popup,
popup_keep_highlighted=True,
style_function=lambda feature: {
"color": self.get_edge_color(feature),
"weight": 4 if feature['properties']['id'] not in self.closed_edges else 6
}
)
# folium.TileLayer('cartodbpositron').add_to(map)
elem.add_to(map)
my_js = f"""{elem.get_name()}.on("click",
function (e) {{
var data = e.latlng;
data.zoom = {map.get_name()}.getZoom()
var data_str = `{{"coordinates": ${{JSON.stringify(data)}}}}`;
console.log(JSON.stringify(data));
}});"""
e = Element(my_js)
html = elem.get_root()
html.script.get_root().render()
# Insert new element or custom JS
html.script._children[e.get_name()] = e
return map
Basically, when I want to refresh the map (When I adde some features in closed_edges set), I invoke the method redraw_folium_map() from a button’s slot. But, nothing happens, despite the folium.GeoJson(..) is correctly invoked, and the method self.get_edge_color() works properly.
I suspect that the web page is not refreshed at all (I tried the various reload methods)