|
| 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