Browse Source

Added handler for WebGL unavailability in map widget (#15640)

* Added handler for WebGL unavailability in map widget

* Changed position of toast

* Additional fixes

* Code improvements
pull/15813/head
Maksym Tsymbarov 1 month ago
committed by GitHub
parent
commit
156ddd113e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 20
      ui-ngx/src/app/modules/home/components/widget/lib/maps/geo-map.ts
  2. 31
      ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet/leaflet-tb.ts
  3. 11
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-layer.ts
  4. 2
      ui-ngx/src/app/shared/models/widget/maps/map.models.ts
  5. 1
      ui-ngx/src/assets/locale/locale.constant-en_US.json

20
ui-ngx/src/app/modules/home/components/widget/lib/maps/geo-map.ts

@ -18,13 +18,13 @@ import {
DEFAULT_ZOOM_LEVEL,
defaultGeoMapSettings,
GeoMapSettings,
latLngPointToBounds,
latLngPointToBounds, MapControlsPosition,
MapZoomAction,
TbCircleData,
TbPolygonCoordinate,
TbPolygonCoordinates,
TbPolygonRawCoordinate,
TbPolygonRawCoordinates
TbPolygonRawCoordinates, WEBGL_ERROR_EVENT
} from '@shared/models/widget/maps/map.models';
import { WidgetContext } from '@home/models/widget-component.models';
import { DeepPartial } from '@shared/models/common';
@ -92,9 +92,19 @@ export class TbGeoMap extends TbMap<GeoMapSettings> {
return this.loadLayers().pipe(
tap((layers: L.TB.LayerData[]) => {
if (layers.length) {
const layer = layers[0];
layer.layer.addTo(this.map);
this.map.attributionControl.setPrefix(layer.attributionPrefix);
const defaultLayer = layers[0];
layers.forEach(layer => {
layer.layer.once(WEBGL_ERROR_EVENT, () => {
const toastPosition = this.settings.controlsPosition === MapControlsPosition.bottomleft
|| this.settings.controlsPosition === MapControlsPosition.bottomright ? 'top' : 'bottom';
this.ctx.showErrorToast(
this.ctx.translate.instant('widgets.maps.layer.webgl-not-available'),
toastPosition, 'left', this.ctx.toastTargetId, true
);
});
});
defaultLayer.layer.addTo(this.map);
this.map.attributionControl.setPrefix(defaultLayer.attributionPrefix);
if (layers.length > 1) {
const sidebar = this.getSidebar();
L.TB.layers({

31
ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet/leaflet-tb.ts

@ -24,6 +24,7 @@ import { MatIconRegistry } from '@angular/material/icon';
import { isSvgIcon, splitIconName } from '@shared/models/icon.models';
import { catchError, take } from 'rxjs/operators';
import { of } from 'rxjs';
import { WEBGL_ERROR_EVENT } from '@shared/models/widget/maps/map.models';
L.MarkerCluster = L.MarkerCluster.mergeOptions({ pmIgnore: true });
@ -1093,6 +1094,7 @@ class MapLibreGLLayer extends L.Layer implements TB.MapLibreGL.MapLibreGLLayer {
private _actualCanvas: HTMLCanvasElement;
private _offset: L.Point;
private _zooming: boolean;
private _glError = false;
constructor(options: TB.MapLibreGL.LeafletMapLibreGLMapOptions) {
super();
@ -1136,7 +1138,9 @@ class MapLibreGLLayer extends L.Layer implements TB.MapLibreGL.MapLibreGLLayer {
const paneName = this.getPaneName();
map.getPane(paneName).removeChild(this._container);
this._glMap.remove();
if (this._glMap) {
this._glMap.remove();
}
this._glMap = null;
return this;
@ -1204,7 +1208,18 @@ class MapLibreGLLayer extends L.Layer implements TB.MapLibreGL.MapLibreGLLayer {
zoom: this._map.getZoom() - 1,
attributionControl: false
});
this._glMap = new MapLibreGLMap(options);
this._glError = false;
try {
this._glMap = new MapLibreGLMap(options);
} catch (e) {
this._glError = true;
this.fire(WEBGL_ERROR_EVENT, { error: e });
return;
}
this._glMap.once('webglcontextlost', (e) => {
this._glError = true;
this.fire(WEBGL_ERROR_EVENT, {error: e});
});
this._glMap.once('load', () => {
this.fire('load');
});
@ -1222,8 +1237,12 @@ class MapLibreGLLayer extends L.Layer implements TB.MapLibreGL.MapLibreGLLayer {
}
}
private _glReady(): boolean {
return !this._glError && !!this._glMap;
}
private _update() {
if (!this._map) {
if (!this._map || !this._glReady()) {
return;
}
this._offset = this._map.containerPointToLayerPoint([0, 0]);
@ -1253,6 +1272,7 @@ class MapLibreGLLayer extends L.Layer implements TB.MapLibreGL.MapLibreGLLayer {
private _transformGL(gl: MapLibreGLMap) {
const center = this._map.getCenter();
const tr = gl._getTransformForUpdate();
if (!tr) { return; }
tr.setCenter(MapLibreGLLngLat.convert([center.lng, center.lat]));
tr.setZoom(this._map.getZoom() - 1);
gl.transform.apply(tr);
@ -1260,6 +1280,7 @@ class MapLibreGLLayer extends L.Layer implements TB.MapLibreGL.MapLibreGLLayer {
}
private _pinchZoom() {
if (!this._glReady()) { return; }
this._glMap.jumpTo({
zoom: this._map.getZoom() - 1,
center: this._map.getCenter()
@ -1267,6 +1288,7 @@ class MapLibreGLLayer extends L.Layer implements TB.MapLibreGL.MapLibreGLLayer {
}
private _animateZoom(e: L.ZoomAnimEvent) {
if (!this._glReady() || !this._actualCanvas) { return; }
const scale = this._map.getZoomScale(e.zoom);
const padding = this._map.getSize().multiplyBy(this.options.padding * scale);
const viewHalf = this.getSize().divideBy(2);
@ -1291,6 +1313,7 @@ class MapLibreGLLayer extends L.Layer implements TB.MapLibreGL.MapLibreGLLayer {
}
private _zoomEnd() {
if (!this._glReady() || !this._actualCanvas) { return; }
const scale = this._map.getZoomScale(this._map.getZoom());
L.DomUtil.setTransform(
this._actualCanvas,
@ -1302,7 +1325,9 @@ class MapLibreGLLayer extends L.Layer implements TB.MapLibreGL.MapLibreGLLayer {
}
private _transitionEnd() {
if (!this._glReady()) { return; }
L.Util.requestAnimFrame(() => {
if (!this._glReady()) { return; }
const zoom = this._map.getZoom();
const center = this._map.getCenter();
const offset = this._map.latLngToContainerPoint(

11
ui-ngx/src/app/modules/home/components/widget/lib/maps/map-layer.ts

@ -28,14 +28,14 @@ import {
MapProvider,
OpenStreetMapLayerSettings,
ReferenceLayerType,
TencentMapLayerSettings
TencentMapLayerSettings, WEBGL_ERROR_EVENT
} from '@shared/models/widget/maps/map.models';
import { WidgetContext } from '@home/models/widget-component.models';
import { DeepPartial } from '@shared/models/common';
import { mergeDeep } from '@core/utils';
import { Observable, of, shareReplay, switchMap } from 'rxjs';
import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe';
import L from 'leaflet';
import L, { LeafletEvent } from 'leaflet';
import { catchError, map } from 'rxjs/operators';
import { ResourcesService } from '@core/services/resources.service';
import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec';
@ -120,6 +120,13 @@ export abstract class TbMapLayer<S extends MapLayerSettings> {
let referenceLayerLoaded = false;
baseLayer.addTo(layer);
referenceLayer.addTo(layer);
// Forwarding is needed only here: when a reference layer is present the base layer is
// wrapped in this featureGroup, and geo-map listens for WEBGL_ERROR_EVENT on the
// returned layer (the group). In the other branches the MapLibreGLLayer is returned
// directly and fires WEBGL_ERROR_EVENT itself, so no forwarding is required.
baseLayer.once(WEBGL_ERROR_EVENT, (e: LeafletEvent) => {
layer.fire(WEBGL_ERROR_EVENT, e);
});
baseLayer.once('load', () => {
baseLayerLoaded = true;
if (referenceLayerLoaded) {

2
ui-ngx/src/app/shared/models/widget/maps/map.models.ts

@ -1127,6 +1127,8 @@ export interface ImageMapSourceSettings {
entityKey?: DataKey;
}
export const WEBGL_ERROR_EVENT = 'gl-error';
export const imageMapSourceSettingsValid = (imageSource: ImageMapSourceSettings): boolean => {
if (!imageSource?.sourceType) {
return false;

1
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -8538,6 +8538,7 @@
"roadmap": "Roadmap",
"satellite": "Satellite",
"hybrid": "Hybrid",
"webgl-not-available": "The selected map layer requires graphics acceleration to display. Please enable 'Hardware acceleration' in your browser settings, or switch to a different layer.",
"reference": {
"reference-layer": "Reference layer",
"no-layer": "No layer",

Loading…
Cancel
Save