Skip to content

Commit ae6046a

Browse files
committed
Fix maplibre overwriting all other plugins
1 parent 91bbcd4 commit ae6046a

File tree

2 files changed

+353
-5
lines changed

2 files changed

+353
-5
lines changed

plugins/maplibre-gl-leaflet/src/main/java/software/xdev/vaadin/maps/leaflet/maplibregl/layer/vector/LMaplibreGL.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package software.xdev.vaadin.maps.leaflet.maplibregl.layer.vector;
1717

18+
import com.vaadin.flow.component.dependency.CssImport;
19+
import com.vaadin.flow.component.dependency.JsModule;
1820
import com.vaadin.flow.component.dependency.NpmPackage;
1921

2022
import software.xdev.vaadin.maps.leaflet.layer.LLayer;
@@ -27,11 +29,9 @@
2729
* @see <a href="https://github.com/maplibre/maplibre-gl-leaflet/blob/main/API.md">MapLibre-GL-Leaflet API docs</a>
2830
*/
2931
@NpmPackage(value = "maplibre-gl", version = "5.6.1")
30-
@NpmPackage(value = "@maplibre/maplibre-gl-leaflet", version = "0.1.3")
31-
// TODO MAPLIBRE OVERWRITES EVERYTHING ELSE!
32-
// @JsModule("maplibre-gl/dist/maplibre-gl.js")
33-
// @CssImport("maplibre-gl/dist/maplibre-gl.css")
34-
// @JsModule("@maplibre/maplibre-gl-leaflet/leaflet-maplibre-gl.js")
32+
@JsModule("maplibre-gl/dist/maplibre-gl.js")
33+
@CssImport("maplibre-gl/dist/maplibre-gl.css")
34+
@JsModule("./src/leaflet-maplibre-gl.js") // Use bundled forked version to load it correctly
3535
public class LMaplibreGL extends LLayer<LMaplibreGL>
3636
{
3737
public LMaplibreGL(
Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
/**
2+
* This is a fork of https://github.com/maplibre/maplibre-gl-leaflet/blob/v0.1.3/leaflet-maplibre-gl.js
3+
*
4+
* It was forked so that it's loaded correctly because the default version overwrites all other plugins and brings it's own leaflet (generates "requireLeafletSrc").
5+
*
6+
* Modifications:
7+
* 1. Added "import maplibregl from 'maplibre-gl';" to fix maplibre being loaded correctly
8+
* 2. Changed the dependency loading (code between "(function (root, factory) {... }(typeof globalThis !== 'undefined'") to use above import correctly
9+
*/
10+
import maplibregl from 'maplibre-gl';
11+
12+
(function (root, factory) {
13+
root.returnExports = factory(root.L, maplibregl);
14+
}(typeof globalThis !== 'undefined' ? globalThis : this || self, function (L, maplibregl) {
15+
L.MaplibreGL = L.Layer.extend({
16+
options: {
17+
updateInterval: 32,
18+
// How much to extend the overlay view (relative to map size)
19+
// e.g. 0.1 would be 10% of map view in each direction
20+
padding: 0.1,
21+
// whether or not to register the mouse and keyboard
22+
// events on the maplibre overlay
23+
interactive: false,
24+
// set the tilepane as the default pane to draw gl tiles
25+
pane: 'tilePane'
26+
},
27+
28+
initialize: function (options) {
29+
L.setOptions(this, options);
30+
31+
// setup throttling the update event when panning
32+
this._throttledUpdate = L.Util.throttle(this._update, this.options.updateInterval, this);
33+
},
34+
35+
onAdd: function (map) {
36+
if (!this._container) {
37+
this._initContainer();
38+
}
39+
40+
var paneName = this.getPaneName();
41+
map.getPane(paneName).appendChild(this._container);
42+
43+
this._initGL();
44+
45+
this._offset = this._map.containerPointToLayerPoint([0, 0]);
46+
47+
// work around https://github.com/mapbox/mapbox-gl-leaflet/issues/47
48+
if (map.options.zoomAnimation) {
49+
L.DomEvent.on(map._proxy, L.DomUtil.TRANSITION_END, this._transitionEnd, this);
50+
}
51+
},
52+
53+
onRemove: function (map) {
54+
if (this._map._proxy && this._map.options.zoomAnimation) {
55+
L.DomEvent.off(this._map._proxy, L.DomUtil.TRANSITION_END, this._transitionEnd, this);
56+
}
57+
var paneName = this.getPaneName();
58+
map.getPane(paneName).removeChild(this._container);
59+
60+
this._glMap.remove();
61+
this._glMap = null;
62+
},
63+
64+
getEvents: function () {
65+
return {
66+
move: this._throttledUpdate, // sensibly throttle updating while panning
67+
zoomanim: this._animateZoom, // applys the zoom animation to the <canvas>
68+
zoom: this._pinchZoom, // animate every zoom event for smoother pinch-zooming
69+
zoomstart: this._zoomStart, // flag starting a zoom to disable panning
70+
zoomend: this._zoomEnd,
71+
resize: this._resize
72+
};
73+
},
74+
75+
// https://leafletjs.com/reference.html#layer-getattribution
76+
getAttribution: function () {
77+
// Return custom attribution if specified in options
78+
if (this.options.attributionControl) {
79+
return this.options.attributionControl.customAttribution;
80+
}
81+
82+
// Gather attributions from MapLibre styles
83+
var map = this._glMap;
84+
if (map && this.options.attributionControl !== false) {
85+
var style = map.getStyle();
86+
if (style && style.sources) {
87+
return Object.keys(style.sources)
88+
.map(function (sourceId) {
89+
var source = map.getSource(sourceId);
90+
return (source && typeof source.attribution === 'string') ? source.attribution.trim() : null;
91+
})
92+
.filter(Boolean) // Remove null/undefined values
93+
.join(', ');
94+
}
95+
}
96+
97+
return '';
98+
},
99+
100+
getMaplibreMap: function () {
101+
return this._glMap;
102+
},
103+
104+
getCanvas: function () {
105+
return this._glMap.getCanvas();
106+
},
107+
108+
getSize: function () {
109+
return this._map.getSize().multiplyBy(1 + this.options.padding * 2);
110+
},
111+
112+
getBounds: function () {
113+
var halfSize = this.getSize().multiplyBy(0.5);
114+
var center = this._map.latLngToContainerPoint(this._map.getCenter());
115+
return L.latLngBounds(
116+
this._map.containerPointToLatLng(center.subtract(halfSize)),
117+
this._map.containerPointToLatLng(center.add(halfSize))
118+
);
119+
},
120+
121+
getContainer: function () {
122+
return this._container;
123+
},
124+
125+
// returns the pane name set in options if it is a valid pane, defaults to tilePane
126+
getPaneName: function () {
127+
return this._map.getPane(this.options.pane) ? this.options.pane : 'tilePane';
128+
},
129+
130+
_roundPoint: function (p) {
131+
return { x: Math.round(p.x), y: Math.round(p.y) };
132+
},
133+
134+
_initContainer: function () {
135+
var container = this._container = L.DomUtil.create('div', 'leaflet-gl-layer');
136+
this._resizeContainer();
137+
138+
var offset = this._map.getSize().multiplyBy(this.options.padding);
139+
140+
var topLeft = this._map.containerPointToLayerPoint([0, 0]).subtract(offset);
141+
142+
L.DomUtil.setPosition(container, this._roundPoint(topLeft));
143+
},
144+
145+
_resizeContainer: function () {
146+
var size = this.getSize();
147+
this._container.style.width = size.x + 'px';
148+
this._container.style.height = size.y + 'px';
149+
},
150+
151+
_initGL: function () {
152+
var center = this._map.getCenter();
153+
154+
var options = L.extend({}, this.options, {
155+
container: this._container,
156+
center: [center.lng, center.lat],
157+
zoom: this._map.getZoom() - 1,
158+
attributionControl: false
159+
});
160+
161+
this._glMap = new maplibregl.Map(options);
162+
163+
var _map = this._map;
164+
var _currentAttribution = this.getAttribution();
165+
var _getAttribution = this.getAttribution.bind(this);
166+
this._glMap.on('load', function () {
167+
// Force attribution update
168+
if (_map && _map.attributionControl) {
169+
_map.attributionControl.removeAttribution(_currentAttribution);
170+
_map.attributionControl.addAttribution(_getAttribution());
171+
}
172+
});
173+
174+
// allow GL base map to pan beyond min/max latitudes
175+
// Defensively check if properties are writable before setting them,
176+
// ensuring compatibility with both old and new versions of MapLibre GL JS.
177+
var transformProto = Object.getPrototypeOf(this._glMap.transform);
178+
179+
var latRangeDescriptor = Object.getOwnPropertyDescriptor(transformProto, 'latRange');
180+
if (!latRangeDescriptor || latRangeDescriptor.set || latRangeDescriptor.writable) {
181+
this._glMap.transform.latRange = null;
182+
}
183+
184+
// Although this property is obsolete in modern versions, we apply the same
185+
// defensive check for robust backward compatibility.
186+
var maxValidLatitudeDescriptor = Object.getOwnPropertyDescriptor(transformProto, 'maxValidLatitude');
187+
if (!maxValidLatitudeDescriptor || maxValidLatitudeDescriptor.set || maxValidLatitudeDescriptor.writable) {
188+
this._glMap.transform.maxValidLatitude = Infinity;
189+
}
190+
191+
192+
// check for the existence of _helper and _latRange in MapLibre
193+
// this supports MapLibre v5
194+
if (this._glMap.transform._helper && this._glMap.transform._helper._latRange) {
195+
this._glMap.transform._helper._latRange = [-Infinity, Infinity];
196+
}
197+
198+
this._transformGL(this._glMap);
199+
200+
if (this._glMap._canvas.canvas) {
201+
// older versions of mapbox-gl surfaced the canvas differently
202+
this._glMap._actualCanvas = this._glMap._canvas.canvas;
203+
} else {
204+
this._glMap._actualCanvas = this._glMap._canvas;
205+
}
206+
207+
208+
// treat child <canvas> element like L.ImageOverlay
209+
var canvas = this._glMap._actualCanvas;
210+
L.DomUtil.addClass(canvas, 'leaflet-image-layer');
211+
L.DomUtil.addClass(canvas, 'leaflet-zoom-animated');
212+
213+
if (this.options.interactive) {
214+
L.DomUtil.addClass(canvas, 'leaflet-interactive');
215+
}
216+
if (this.options.className) {
217+
L.DomUtil.addClass(canvas, this.options.className);
218+
}
219+
},
220+
221+
_update: function (e) {
222+
if (!this._map) {
223+
return;
224+
}
225+
// update the offset so we can correct for it later when we zoom
226+
this._offset = this._map.containerPointToLayerPoint([0, 0]);
227+
228+
if (this._zooming) {
229+
return;
230+
}
231+
232+
var container = this._container,
233+
gl = this._glMap,
234+
offset = this._map.getSize().multiplyBy(this.options.padding),
235+
topLeft = this._map.containerPointToLayerPoint([0, 0]).subtract(offset);
236+
237+
L.DomUtil.setPosition(container, this._roundPoint(topLeft));
238+
239+
this._transformGL(gl);
240+
},
241+
242+
_transformGL: function (gl) {
243+
var center = this._map.getCenter();
244+
245+
// gl.setView([center.lat, center.lng], this._map.getZoom() - 1, 0);
246+
// calling setView directly causes sync issues because it uses requestAnimFrame
247+
248+
var tr = gl._getTransformForUpdate(); // .clone() ?
249+
250+
if (tr.setCenter) {
251+
// maplibre 5.0.0 and higher:
252+
tr.setCenter(maplibregl.LngLat.convert([center.lng, center.lat]));
253+
tr.setZoom(this._map.getZoom() - 1);
254+
gl.transform.apply(tr);
255+
} else {
256+
// maplibre < 5.0.0
257+
tr = gl.transform;
258+
tr.center = maplibregl.LngLat.convert([center.lng, center.lat]);
259+
tr.zoom = this._map.getZoom() - 1;
260+
}
261+
262+
gl._fireMoveEvents();
263+
},
264+
265+
// update the map constantly during a pinch zoom
266+
_pinchZoom: function (e) {
267+
this._glMap.jumpTo({
268+
zoom: this._map.getZoom() - 1,
269+
center: this._map.getCenter()
270+
});
271+
},
272+
273+
// borrowed from L.ImageOverlay
274+
// https://github.com/Leaflet/Leaflet/blob/master/src/layer/ImageOverlay.js#L139-L144
275+
_animateZoom: function (e) {
276+
var scale = this._map.getZoomScale(e.zoom);
277+
var padding = this._map.getSize().multiplyBy(this.options.padding * scale);
278+
var viewHalf = this.getSize()._divideBy(2);
279+
// corrections for padding (scaled), adapted from
280+
// https://github.com/Leaflet/Leaflet/blob/master/src/map/Map.js#L1490-L1508
281+
var topLeft = this._map.project(e.center, e.zoom)
282+
._subtract(viewHalf)
283+
._add(this._map._getMapPanePos()
284+
.add(padding))._round();
285+
var offset = this._map.project(this._map.getBounds().getNorthWest(), e.zoom)
286+
._subtract(topLeft);
287+
288+
L.DomUtil.setTransform(
289+
this._glMap._actualCanvas,
290+
offset.subtract(this._offset),
291+
scale
292+
);
293+
},
294+
295+
_zoomStart: function (e) {
296+
this._zooming = true;
297+
},
298+
299+
_zoomEnd: function () {
300+
var scale = this._map.getZoomScale(this._map.getZoom());
301+
302+
L.DomUtil.setTransform(
303+
this._glMap._actualCanvas,
304+
// https://github.com/mapbox/mapbox-gl-leaflet/pull/130
305+
null,
306+
scale
307+
);
308+
309+
this._zooming = false;
310+
311+
this._update();
312+
},
313+
314+
_transitionEnd: function (e) {
315+
L.Util.requestAnimFrame(function () {
316+
var zoom = this._map.getZoom();
317+
var center = this._map.getCenter();
318+
var offset = this._map.latLngToContainerPoint(
319+
this._map.getBounds().getNorthWest()
320+
);
321+
this._resizeContainer();
322+
323+
// reset the scale and offset
324+
L.DomUtil.setTransform(this._glMap._actualCanvas, offset, 1);
325+
326+
// enable panning once the gl map is ready again
327+
this._glMap.once('moveend', L.Util.bind(function () {
328+
this._zoomEnd();
329+
}, this));
330+
331+
// update the map position
332+
this._glMap.jumpTo({
333+
center: center,
334+
zoom: zoom - 1
335+
});
336+
}, this);
337+
},
338+
339+
_resize: function (e) {
340+
this._transitionEnd(e);
341+
}
342+
});
343+
344+
L.maplibreGL = function (options) {
345+
return new L.MaplibreGL(options);
346+
};
347+
348+
}));

0 commit comments

Comments
 (0)