mirror of
https://github.com/LorenEteval/Furious.git
synced 2026-10-05 21:37:56 +03:00
Stabilize and enrich endpoint map rendering
Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user