From a58df7c446417d7bdade393c2d6bdd6c25a2523e Mon Sep 17 00:00:00 2001 From: tariqksoliman Date: Wed, 4 Oct 2023 14:53:17 -0700 Subject: [PATCH] #432 Image Overlay Fixes --- .../Basics/Layers_/LayerConstructors.js | 20 +- src/essence/Basics/Layers_/Layers_.js | 2 +- .../Leaflet/leaflet-imageoverlay-rotated.js | 292 ++++++++++-------- .../Leaflet/leaflet-imagetransform.js | 85 +++-- 4 files changed, 227 insertions(+), 172 deletions(-) diff --git a/src/essence/Basics/Layers_/LayerConstructors.js b/src/essence/Basics/Layers_/LayerConstructors.js index 7fd14b98..d45332e2 100644 --- a/src/essence/Basics/Layers_/LayerConstructors.js +++ b/src/essence/Basics/Layers_/LayerConstructors.js @@ -1238,16 +1238,28 @@ const imageOverlays = (geojson, layerObj, leafletLayerObject) => { L.imageTransform(imageSettings.image, anchors, { opacity: 1, clip: anchors, + id: `${layerObj.name}${ + imageSettings.image + }${angle}${JSON.stringify(center)}`, }), ]) }, } + let existingOn = null + if (L_.layers.attachments[L_.asLayerUUID(layerObj.name)]) + existingOn = + L_.layers.attachments[L_.asLayerUUID(layerObj.name)] + .image_overlays.on + + const isOn = + existingOn != null + ? existingOn + : imageVar.initialVisibility != null + ? imageVar.initialVisibility + : true return imageShow === 'always' ? { - on: - imageVar.initialVisibility != null - ? imageVar.initialVisibility - : true, + on: isOn, layer: L.geoJson(geojson, leafletLayerObjectImageOverlay), title: 'Map rendered image overlays.', } diff --git a/src/essence/Basics/Layers_/Layers_.js b/src/essence/Basics/Layers_/Layers_.js index d08f2a1e..52bb8a3a 100644 --- a/src/essence/Basics/Layers_/Layers_.js +++ b/src/essence/Basics/Layers_/Layers_.js @@ -2591,7 +2591,7 @@ const L_ = { return } L_.syncSublayerData(layerName) - L_.globeLithoLayerHelper(L_.layersNamed[layerName]) + L_.globeLithoLayerHelper(L_.layers.layer[layerName]) } else { console.warn( 'Warning: Unable to update vector layer as it does not exist: ' + diff --git a/src/external/Leaflet/leaflet-imageoverlay-rotated.js b/src/external/Leaflet/leaflet-imageoverlay-rotated.js index a54ddcfb..21da4eb1 100644 --- a/src/external/Leaflet/leaflet-imageoverlay-rotated.js +++ b/src/external/Leaflet/leaflet-imageoverlay-rotated.js @@ -1,145 +1,169 @@ - L.ImageOverlay.Rotated = L.ImageOverlay.extend({ - - initialize: function (image, topleft, topright, bottomleft, options) { - - if (typeof(image) === 'string') { - this._url = image; - } else { - // Assume that the first parameter is an instance of HTMLImage or HTMLCanvas - this._rawImage = image; - } - - this._topLeft = L.latLng(topleft); - this._topRight = L.latLng(topright); - this._bottomLeft = L.latLng(bottomleft); - - L.setOptions(this, options); - }, - - - onAdd: function (map) { - if (!this._image) { - this._initImage(); - - if (this.options.opacity < 1) { - this._updateOpacity(); - } - } - - if (this.options.interactive) { - L.DomUtil.addClass(this._rawImage, 'leaflet-interactive'); - this.addInteractiveTarget(this._rawImage); - } - - map.on('zoomend resetview', this._reset, this); - - this.getPane().appendChild(this._image); - this._reset(); - }, - - - onRemove: function(map) { - map.off('zoomend resetview', this._reset, this); - L.ImageOverlay.prototype.onRemove.call(this, map); + initialize: function (image, topleft, topright, bottomleft, options) { + if (typeof image === 'string') { + this._url = image + } else { + // Assume that the first parameter is an instance of HTMLImage or HTMLCanvas + this._rawImage = image + } + + this._topLeft = L.latLng(topleft) + this._topRight = L.latLng(topright) + this._bottomLeft = L.latLng(bottomleft) + + L.setOptions(this, options) }, - - - _initImage: function () { - var img = this._rawImage; - if (this._url) { - img = L.DomUtil.create('img'); - img.style.display = 'none'; // Hide while the first transform (zero or one frames) is being done - - if (this.options.crossOrigin) { - img.crossOrigin = ''; - } - - img.src = this._url; - this._rawImage = img; - } - L.DomUtil.addClass(img, 'leaflet-image-layer'); - - // this._image is reused by some of the methods of the parent class and - // must keep the name, even if it is counter-intuitive. - var div = this._image = L.DomUtil.create('div', - 'leaflet-image-layer ' + (this._zoomAnimated ? 'leaflet-zoom-animated' : '')); - - div.appendChild(img); - - div.onselectstart = L.Util.falseFn; - div.onmousemove = L.Util.falseFn; - img.onload = function(){ - this._reset(); - img.style.display = 'block'; - this.fire('load'); - }.bind(this); + onAdd: function (map) { + if (!this._image) { + this._initImage() - img.alt = this.options.alt; - }, + if (this.options.opacity < 1) { + this._updateOpacity() + } + } + if (this.options.interactive) { + L.DomUtil.addClass(this._rawImage, 'leaflet-interactive') + this.addInteractiveTarget(this._rawImage) + } - _reset: function () { - var div = this._image; + map.on('zoomend resetview', this._reset, this) - // Project control points to container-pixel coordinates - var pxTopLeft = this._map.latLngToLayerPoint(this._topLeft); - var pxTopRight = this._map.latLngToLayerPoint(this._topRight); - var pxBottomLeft = this._map.latLngToLayerPoint(this._bottomLeft); - - // Infer coordinate of bottom right - var pxBottomRight = pxTopRight.subtract(pxTopLeft).add(pxBottomLeft); - - // pxBounds is mostly for positioning the
container - var pxBounds = L.bounds([pxTopLeft, pxTopRight, pxBottomLeft, pxBottomRight]); - var size = pxBounds.getSize(); - var pxTopLeftInDiv = pxTopLeft.subtract(pxBounds.min); - - // Calculate the skew angles, both in X and Y - var vectorX = pxTopRight.subtract(pxTopLeft); - var vectorY = pxBottomLeft.subtract(pxTopLeft); - var skewX = Math.atan2( vectorX.y, vectorX.x ); - var skewY = Math.atan2( vectorY.x, vectorY.y ); - - // LatLngBounds used for animations - this._bounds = L.latLngBounds( this._map.layerPointToLatLng(pxBounds.min), - this._map.layerPointToLatLng(pxBounds.max) ); - - L.DomUtil.setPosition(div, pxBounds.min); - - div.style.width = size.x + 'px'; - div.style.height = size.y + 'px'; - - var imgW = this._rawImage.width; - var imgH = this._rawImage.height; - if (!imgW || !imgH) { - return; // Probably because the image hasn't loaded yet. - } - - var scaleX = pxTopLeft.distanceTo(pxTopRight) / imgW * Math.cos(skewX); - var scaleY = pxTopLeft.distanceTo(pxBottomLeft) / imgH * Math.cos(skewY); - - this._rawImage.style.transformOrigin = '0 0'; - - this._rawImage.style.transform = - 'translate(' + pxTopLeftInDiv.x + 'px, ' + pxTopLeftInDiv.y + 'px)' + - 'skew(' + skewY + 'rad, ' + skewX + 'rad) ' + - 'scale(' + scaleX + ', ' + scaleY + ') '; - }, - - - reposition: function(topleft, topright, bottomleft) { - this._topLeft = L.latLng(topleft); - this._topRight = L.latLng(topright); - this._bottomLeft = L.latLng(bottomleft); - this._reset(); - } + this.getPane().appendChild(this._image) + this._reset() + }, -}); + onRemove: function (map) { + map.off('zoomend resetview', this._reset, this) + L.ImageOverlay.prototype.onRemove.call(this, map) + }, + _initImage: function () { + var img = this._rawImage + if (this._url) { + img = L.DomUtil.create('img') + img.style.display = 'none' // Hide while the first transform (zero or one frames) is being done + + if (this.options.crossOrigin) { + img.crossOrigin = '' + } + + img.src = this._url + this._rawImage = img + } + L.DomUtil.addClass(img, 'leaflet-image-layer') + + // this._image is reused by some of the methods of the parent class and + // must keep the name, even if it is counter-intuitive. + var div = (this._image = L.DomUtil.create( + 'div', + 'leaflet-image-layer ' + + (this._zoomAnimated ? 'leaflet-zoom-animated' : '') + )) + + div.appendChild(img) + + div.onselectstart = L.Util.falseFn + div.onmousemove = L.Util.falseFn + + img.onload = function () { + this._reset() + img.style.display = 'block' + this.fire('load') + }.bind(this) + + img.alt = this.options.alt + }, + _reset: function () { + var div = this._image + + // Project control points to container-pixel coordinates + var pxTopLeft = this._map.latLngToLayerPoint(this._topLeft) + var pxTopRight = this._map.latLngToLayerPoint(this._topRight) + var pxBottomLeft = this._map.latLngToLayerPoint(this._bottomLeft) + + // Infer coordinate of bottom right + var pxBottomRight = pxTopRight.subtract(pxTopLeft).add(pxBottomLeft) + + // pxBounds is mostly for positioning the
container + var pxBounds = L.bounds([ + pxTopLeft, + pxTopRight, + pxBottomLeft, + pxBottomRight, + ]) + var size = pxBounds.getSize() + var pxTopLeftInDiv = pxTopLeft.subtract(pxBounds.min) + + // Calculate the skew angles, both in X and Y + var vectorX = pxTopRight.subtract(pxTopLeft) + var vectorY = pxBottomLeft.subtract(pxTopLeft) + var skewX = Math.atan2(vectorX.y, vectorX.x) + var skewY = Math.atan2(vectorY.x, vectorY.y) + + // LatLngBounds used for animations + this._bounds = L.latLngBounds( + this._map.layerPointToLatLng(pxBounds.min), + this._map.layerPointToLatLng(pxBounds.max) + ) + + L.DomUtil.setPosition(div, pxBounds.min) + + div.style.width = size.x + 'px' + div.style.height = size.y + 'px' + + var imgW = this._rawImage.width + var imgH = this._rawImage.height + if (!imgW || !imgH) { + return // Probably because the image hasn't loaded yet. + } + + var scaleX = (pxTopLeft.distanceTo(pxTopRight) / imgW) * Math.cos(skewX) + var scaleY = + (pxTopLeft.distanceTo(pxBottomLeft) / imgH) * Math.cos(skewY) + + this._rawImage.style.transformOrigin = '0 0' + + this._rawImage.style.transform = + 'translate(' + + pxTopLeftInDiv.x + + 'px, ' + + pxTopLeftInDiv.y + + 'px)' + + 'skew(' + + skewY + + 'rad, ' + + skewX + + 'rad) ' + + 'scale(' + + scaleX + + ', ' + + scaleY + + ') ' + }, -L.imageOverlay.rotated = function(imgSrc, topleft, topright, bottomleft, options) { - return new L.ImageOverlay.Rotated(imgSrc, topleft, topright, bottomleft, options); -}; + reposition: function (topleft, topright, bottomleft) { + this._topLeft = L.latLng(topleft) + this._topRight = L.latLng(topright) + this._bottomLeft = L.latLng(bottomleft) + this._reset() + }, +}) + +L.imageOverlay.rotated = function ( + imgSrc, + topleft, + topright, + bottomleft, + options +) { + return new L.ImageOverlay.Rotated( + imgSrc, + topleft, + topright, + bottomleft, + options + ) +} diff --git a/src/external/Leaflet/leaflet-imagetransform.js b/src/external/Leaflet/leaflet-imagetransform.js index 61703848..afba8d9c 100644 --- a/src/external/Leaflet/leaflet-imagetransform.js +++ b/src/external/Leaflet/leaflet-imagetransform.js @@ -5,6 +5,7 @@ factory(window.L) } })(function (L) { + const LImageTransformCache = {} L.ImageTransform = L.ImageOverlay.extend({ initialize: function (url, anchors, options) { // (String, LatLngBounds, Object) @@ -70,45 +71,63 @@ _imgLoaded: false, _initImage: function () { - this._image = L.DomUtil.create('div', 'leaflet-image-layer') - - if (this._map.options.zoomAnimation && L.Browser.any3d) { - L.DomUtil.addClass(this._image, 'leaflet-zoom-animated') + const cache = LImageTransformCache[this.options.id] + if (cache) { + this._image = cache._image + this._imgNode = cache._imgNode + this._canvas = cache._canvas + this._onImageLoad() } else { - L.DomUtil.addClass(this._image, 'leaflet-zoom-hide') - } + this._image = L.DomUtil.create('div', 'leaflet-image-layer') - this._imgNode = L.DomUtil.create('img') - if (this.options.clip) { - this._canvas = L.DomUtil.create( - 'canvas', - 'leaflet-canvas-transform' - ) - this._image.appendChild(this._canvas) - this._canvas.style[L.DomUtil.TRANSFORM_ORIGIN] = '0 0' - this._clipDone = false - } else { - this._image.appendChild(this._imgNode) - this._imgNode.style[L.DomUtil.TRANSFORM_ORIGIN] = '0 0' + if (this._map.options.zoomAnimation && L.Browser.any3d) { + L.DomUtil.addClass(this._image, 'leaflet-zoom-animated') + } else { + L.DomUtil.addClass(this._image, 'leaflet-zoom-hide') + } - // Hide imgNode until image has loaded - this._imgNode.style.display = 'none' - } + this._imgNode = L.DomUtil.create('img') + if (this.options.clip) { + this._canvas = L.DomUtil.create( + 'canvas', + 'leaflet-canvas-transform' + ) + this._image.appendChild(this._canvas) + this._canvas.style[L.DomUtil.TRANSFORM_ORIGIN] = '0 0' + this._clipDone = false + } else { + this._image.appendChild(this._imgNode) + this._imgNode.style[L.DomUtil.TRANSFORM_ORIGIN] = '0 0' + + // Hide imgNode until image has loaded + this._imgNode.style.display = 'none' + } + + if (this.options.id != null) { + LImageTransformCache[this.options.id] = { + _image: this._image, + _imgNode: this._imgNode, + _canvas: this._canvas, + } + } - this._updateOpacity() - - //TODO createImage util method to remove duplication - this._imgLoaded = false - L.extend(this._imgNode, { - galleryimg: 'no', - onselectstart: L.Util.falseFn, - onmousemove: L.Util.falseFn, - onload: L.bind(this._onImageLoad, this), - onerror: L.bind(this._onImageError, this), - src: this._url, - }) + this._updateOpacity() + + //TODO createImage util method to remove duplication + this._imgLoaded = false + L.extend(this._imgNode, { + galleryimg: 'no', + onselectstart: L.Util.falseFn, + onmousemove: L.Util.falseFn, + onload: L.bind(this._onImageLoad, this), + onerror: L.bind(this._onImageError, this), + src: this._url, + }) + } }, + createImage: function () {}, + _onImageError: function () { this.fire('error') },