Stabilize and enrich endpoint map rendering

Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
Loren Eteval
2026-08-23 16:41:07 +08:00
parent 38375bcce0
commit 6ec3ef25dc
3 changed files with 414 additions and 58 deletions
+388 -16
View File
@@ -6,11 +6,378 @@
let marker = null;
let state = null;
let appliedRevision = -1;
let activeStyle = '';
let activeDarkMode = null;
let initialReadyReported = false;
const loadingOverlay = document.getElementById('endpoint-loading-overlay');
const loadingText = document.getElementById('endpoint-loading-text');
const nameField = [
'case',
['all', ['has', 'name:latin'], ['has', 'name:nonlatin']],
['concat', ['get', 'name:latin'], '\n', ['get', 'name:nonlatin']],
[
'coalesce',
['get', 'name_en'],
['get', 'name'],
['get', 'name:latin'],
'',
],
];
const createStyle = (darkMode) => {
const colors = darkMode
? {
background: '#0d1117',
land: '#161c25',
landcover: '#18271f',
landuse: '#1b222c',
water: '#19334a',
boundary: '#536174',
road: '#3e4a59',
roadMajor: '#657489',
building: '#222c38',
label: '#eef4fb',
labelMuted: '#b9c5d3',
halo: '#111720',
waterLabel: '#8fc4e8',
poi: '#c6d0dc',
}
: {
background: '#edf1f5',
land: '#f7f8fa',
landcover: '#e2ebdf',
landuse: '#eceff3',
water: '#bad9ec',
boundary: '#a7b0bd',
road: '#ffffff',
roadMajor: '#d4aa72',
building: '#d9dce1',
label: '#202832',
labelMuted: '#536170',
halo: '#ffffff',
waterLabel: '#446f91',
poi: '#4e5b68',
};
return {
version: 8,
glyphs: 'https://tiles.openfreemap.org/fonts/{fontstack}/{range}.pbf',
sources: {
openmaptiles: {
type: 'vector',
url: 'https://tiles.openfreemap.org/planet',
attribution: '<a href="https://openfreemap.org/">OpenFreeMap</a> ' +
'<a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
},
},
layers: [
{
id: 'background',
type: 'background',
paint: {'background-color': colors.background},
},
{
id: 'land',
type: 'fill',
source: 'openmaptiles',
'source-layer': 'landcover',
paint: {'fill-color': colors.land},
},
{
id: 'landcover',
type: 'fill',
source: 'openmaptiles',
'source-layer': 'landcover',
paint: {
'fill-color': colors.landcover,
'fill-opacity': 0.55,
},
},
{
id: 'landuse',
type: 'fill',
source: 'openmaptiles',
'source-layer': 'landuse',
paint: {
'fill-color': colors.landuse,
'fill-opacity': 0.5,
},
},
{
id: 'water',
type: 'fill',
source: 'openmaptiles',
'source-layer': 'water',
paint: {'fill-color': colors.water},
},
{
id: 'boundary',
type: 'line',
source: 'openmaptiles',
'source-layer': 'boundary',
paint: {
'line-color': colors.boundary,
'line-width': 0.8,
'line-opacity': 0.7,
},
},
{
id: 'transportation',
type: 'line',
source: 'openmaptiles',
'source-layer': 'transportation',
minzoom: 5,
paint: {
'line-color': colors.road,
'line-width': 0.8,
'line-opacity': 0.65,
},
},
{
id: 'transportation-major',
type: 'line',
source: 'openmaptiles',
'source-layer': 'transportation',
minzoom: 6,
filter: [
'match',
['get', 'class'],
['motorway', 'trunk', 'primary', 'secondary', 'tertiary'],
true,
false,
],
paint: {
'line-color': colors.roadMajor,
'line-width': [
'interpolate',
['linear'],
['zoom'],
6,
0.8,
14,
2.4,
],
'line-opacity': 0.85,
},
},
{
id: 'building',
type: 'fill',
source: 'openmaptiles',
'source-layer': 'building',
minzoom: 13,
paint: {
'fill-color': colors.building,
'fill-opacity': 0.72,
},
},
{
id: 'water-name',
type: 'symbol',
source: 'openmaptiles',
'source-layer': 'water_name',
minzoom: 3,
layout: {
'symbol-placement': 'point',
'text-field': nameField,
'text-font': ['Noto Sans Italic'],
'text-size': [
'interpolate',
['linear'],
['zoom'],
3,
10,
12,
14,
],
'text-max-width': 8,
},
paint: {
'text-color': colors.waterLabel,
'text-halo-color': colors.halo,
'text-halo-width': 1,
},
},
{
id: 'road-name-major',
type: 'symbol',
source: 'openmaptiles',
'source-layer': 'transportation_name',
minzoom: 11,
filter: [
'match',
['get', 'class'],
['motorway', 'trunk', 'primary', 'secondary', 'tertiary'],
true,
false,
],
layout: {
'symbol-placement': 'line',
'symbol-spacing': 300,
'text-field': nameField,
'text-font': ['Noto Sans Regular'],
'text-size': [
'interpolate',
['linear'],
['zoom'],
11,
10,
16,
13,
],
},
paint: {
'text-color': colors.labelMuted,
'text-halo-color': colors.halo,
'text-halo-width': 1,
},
},
{
id: 'road-name-minor',
type: 'symbol',
source: 'openmaptiles',
'source-layer': 'transportation_name',
minzoom: 14,
filter: [
'match',
['get', 'class'],
['minor', 'service', 'track', 'path'],
true,
false,
],
layout: {
'symbol-placement': 'line',
'symbol-spacing': 260,
'text-field': nameField,
'text-font': ['Noto Sans Regular'],
'text-size': 11,
},
paint: {
'text-color': colors.labelMuted,
'text-halo-color': colors.halo,
'text-halo-width': 1,
},
},
{
id: 'place-country',
type: 'symbol',
source: 'openmaptiles',
'source-layer': 'place',
minzoom: 2,
maxzoom: 9,
filter: ['==', ['get', 'class'], 'country'],
layout: {
'text-field': nameField,
'text-font': ['Noto Sans Bold'],
'text-size': [
'interpolate',
['linear'],
['zoom'],
2,
10,
7,
16,
],
'text-max-width': 7,
},
paint: {
'text-color': colors.label,
'text-halo-color': colors.halo,
'text-halo-width': 1.2,
},
},
{
id: 'place-state',
type: 'symbol',
source: 'openmaptiles',
'source-layer': 'place',
minzoom: 4,
maxzoom: 10,
filter: ['==', ['get', 'class'], 'state'],
layout: {
'text-field': nameField,
'text-font': ['Noto Sans Italic'],
'text-size': 11,
'text-letter-spacing': 0.08,
'text-max-width': 8,
},
paint: {
'text-color': colors.labelMuted,
'text-halo-color': colors.halo,
'text-halo-width': 1,
},
},
{
id: 'place-city',
type: 'symbol',
source: 'openmaptiles',
'source-layer': 'place',
minzoom: 3,
filter: [
'match',
['get', 'class'],
['city', 'town', 'village'],
true,
false,
],
layout: {
'text-field': nameField,
'text-font': ['Noto Sans Regular'],
'text-size': [
'interpolate',
['linear'],
['zoom'],
4,
11,
12,
15,
],
'text-max-width': 8,
'text-variable-anchor': ['top', 'bottom', 'left', 'right'],
'text-radial-offset': 0.4,
},
paint: {
'text-color': colors.label,
'text-halo-color': colors.halo,
'text-halo-width': 1.2,
},
},
{
id: 'poi-name',
type: 'symbol',
source: 'openmaptiles',
'source-layer': 'poi',
minzoom: 15,
layout: {
'text-field': nameField,
'text-font': ['Noto Sans Regular'],
'text-size': 10,
'text-max-width': 9,
},
paint: {
'text-color': colors.poi,
'text-halo-color': colors.halo,
'text-halo-width': 1,
},
},
],
};
};
const coordinate = () => {
if (!state || !state.markerVisible) {
return null;
}
const longitude = Number(state.markerLongitude);
const latitude = Number(state.markerLatitude);
if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) {
return null;
}
return [longitude, latitude];
};
const applyLoadingState = () => {
const loading = Boolean(state && state.loading);
loadingOverlay.dataset.visible = String(loading);
@@ -37,7 +404,8 @@
};
const ensureMarker = () => {
if (!map || !state || !state.markerVisible) {
const markerCoordinate = coordinate();
if (!map || !markerCoordinate) {
if (marker) {
marker.remove();
marker = null;
@@ -52,7 +420,7 @@
}
marker
.setLngLat([state.markerLongitude, state.markerLatitude])
.setLngLat(markerCoordinate)
.addTo(map);
};
@@ -84,18 +452,18 @@
return;
}
const nextStyle = state.darkMode
? state.darkStyleUrl
: state.lightStyleUrl;
if (nextStyle !== activeStyle) {
activeStyle = nextStyle;
const nextDarkMode = Boolean(state.darkMode);
if (nextDarkMode !== activeDarkMode) {
activeDarkMode = nextDarkMode;
const nextStyle = createStyle(nextDarkMode);
map.setStyle(nextStyle);
}
if (state.viewRevision !== appliedRevision) {
const nextCoordinate = coordinate();
if (nextCoordinate && state.viewRevision !== appliedRevision) {
appliedRevision = state.viewRevision;
map.jumpTo({
center: [state.markerLongitude, state.markerLatitude],
center: nextCoordinate,
zoom: state.defaultGeographicZoom,
});
}
@@ -105,20 +473,24 @@
};
const createMap = () => {
if (map || !state || !state.markerVisible) {
const initialCoordinate = coordinate();
if (map || !initialCoordinate) {
return;
}
activeStyle = state.darkMode
? state.darkStyleUrl
: state.lightStyleUrl;
activeDarkMode = Boolean(state.darkMode);
map = new maplibregl.Map({
container: 'map',
style: activeStyle,
center: [state.markerLongitude, state.markerLatitude],
style: createStyle(activeDarkMode),
center: initialCoordinate,
zoom: state.defaultGeographicZoom,
minZoom: 2,
maxZoom: 18,
maxTileCacheSize: 64,
canvasContextAttributes: {
antialias: false,
powerPreference: 'low-power',
},
attributionControl: true,
cooperativeGestures: false,
fadeDuration: 0,
+13 -18
View File
@@ -35,7 +35,6 @@ from PySide6.QtWebEngineCore import (
from PySide6.QtWebEngineWidgets import QWebEngineView
from PySide6.QtWidgets import *
import enum
import json
import logging
@@ -82,20 +81,7 @@ class _EndpointMapBridge(QtCore.QObject):
class _EndpointMapWidget(QWidget):
"""Host one lazily loaded MapLibre vector map for the page lifetime."""
class Style(enum.StrEnum):
"""OpenFreeMap styles available to the embedded endpoint map."""
Bright, Liberty, Positron, Dark, Fiord = (
'https://tiles.openfreemap.org/styles/bright',
'https://tiles.openfreemap.org/styles/liberty',
'https://tiles.openfreemap.org/styles/positron',
'https://tiles.openfreemap.org/styles/dark',
'https://tiles.openfreemap.org/styles/fiord',
)
HtmlPath = DATA_DIR / 'maplibre' / 'EndpointMap.html'
LightStyle = Style.Liberty
DarkStyle = Style.Fiord
TrustedAttributionHosts = frozenset(
{
'openfreemap.org',
@@ -123,6 +109,7 @@ class _EndpointMapWidget(QWidget):
self._documentLoaded = False
self._mapReady = False
self._mapError = ''
self._rendererFailureHandled = False
self._viewRevision = 0
self._lastWebState = {}
self._retainMapDuringRefresh = False
@@ -350,10 +337,15 @@ class _EndpointMapWidget(QWidget):
"""Publish local-document failures and send the first map state."""
self._documentLoaded = bool(successful)
if not successful:
if successful:
self._rendererFailureHandled = False
else:
self._mapError = self._unavailableText or 'Endpoint map unavailable'
logger.error('failed to load the local endpoint map document')
# A renderer crash also completes the outstanding load with False.
# The termination handler already records that more precise cause.
if not self._rendererFailureHandled:
logger.error('failed to load the local endpoint map document')
self._syncWebState()
self._updateOverlay()
@@ -378,6 +370,11 @@ class _EndpointMapWidget(QWidget):
@QtCore.Slot(QWebEnginePage.RenderProcessTerminationStatus, int)
def _renderProcessTerminated(self, status, exitCode):
"""Convert an unexpected Chromium exit into the normal map fallback."""
if self._rendererFailureHandled:
return
self._rendererFailureHandled = True
self._documentLoaded = False
self._mapReady = False
self._mapError = self._unavailableText or 'Endpoint map unavailable'
@@ -416,8 +413,6 @@ class _EndpointMapWidget(QWidget):
'defaultGeographicZoom': self.DefaultGeographicZoom,
'viewRevision': self._viewRevision,
'darkMode': self._theme == AppStyleSheet.Dark,
'lightStyleUrl': self.LightStyle.value,
'darkStyleUrl': self.DarkStyle.value,
'accentColor': accentColor,
'loading': self._loading
or (
+13 -24
View File
@@ -805,26 +805,19 @@ class EndpointInfoServiceTest(unittest.TestCase):
self.assertIn('.maplibregl-canvas:focus', html)
self.assertIn('outline: none', html)
self.assertNotIn('color-mix(', html)
self.assertEqual(
{style.name: style.value for style in widget.mapWidget.Style},
{
'Bright': 'https://tiles.openfreemap.org/styles/bright',
'Liberty': 'https://tiles.openfreemap.org/styles/liberty',
'Positron': 'https://tiles.openfreemap.org/styles/positron',
'Dark': 'https://tiles.openfreemap.org/styles/dark',
'Fiord': 'https://tiles.openfreemap.org/styles/fiord',
},
)
self.assertEqual(
widget.mapWidget._lastWebState['lightStyleUrl'],
widget.mapWidget.LightStyle.value,
)
self.assertIs(widget.mapWidget.LightStyle, widget.mapWidget.Style.Liberty)
self.assertEqual(
widget.mapWidget._lastWebState['darkStyleUrl'],
widget.mapWidget.DarkStyle.value,
)
self.assertIs(widget.mapWidget.DarkStyle, widget.mapWidget.Style.Fiord)
self.assertIn('const createStyle = (darkMode)', mapScript)
self.assertIn("url: 'https://tiles.openfreemap.org/planet'", mapScript)
self.assertIn("glyphs: 'https://tiles.openfreemap.org/fonts/", mapScript)
self.assertIn("'source-layer': 'water'", mapScript)
self.assertIn("'source-layer': 'building'", mapScript)
self.assertIn("'source-layer': 'transportation_name'", mapScript)
self.assertIn("'source-layer': 'place'", mapScript)
self.assertIn("'source-layer': 'poi'", mapScript)
self.assertIn("'text-field': nameField", mapScript)
self.assertIn('Number.isFinite(longitude)', mapScript)
self.assertNotIn('lightStyleUrl', widget.mapWidget._lastWebState)
self.assertNotIn('darkStyleUrl', widget.mapWidget._lastWebState)
self.assertNotIn('/styles/fiord', mapScript)
self.assertNotIn('tile.openstreetmap.org', html)
self.assertNotIn('opacity: 0.60', html)
self.assertNotIn('opacity: 0.60', mapScript)
@@ -948,10 +941,6 @@ class EndpointInfoServiceTest(unittest.TestCase):
self.assertEqual(widget.mapWidget._theme, AppStyleSheet.Light)
self.assertFalse(widget.mapWidget._lastWebState['darkMode'])
self.assertEqual(
widget.mapWidget._lastWebState['lightStyleUrl'],
widget.mapWidget.LightStyle.value,
)
self.assertTrue(scripts)
widget.close()