0

I'm trying to render the two images on side by side comparison view.

var map = L.map("map", {
  center: [23.14, -101.887],
  zoom: 5
});

map.createPane("left");
map.createPane("right");

var catedralVieja =
    "http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg",
  imageBounds = [
    [7.9409, -131.1589],
    [29.2144, -82.6558]
  ];
var leftLayer = L.imageOverlay(catedralVieja, imageBounds, {
  pane: "left"
}).addTo(map);

var catedralNueva = "https://placekitten.com/g/1200/1080",
  imageBounds = [
    [7.9409, -131.1589],
    [29.2144, -82.6558]
  ];
var rightLayer = L.imageOverlay(catedralNueva, imageBounds, {
  pane: "right"
}).addTo(map);

// Not working
// L.control.sideBySide(leftLayer, rightLayer).addTo(map);

L.control
  .sideBySide(L.featureGroup(leftLayer), L.featureGroup(rightLayer))
  .addTo(map);

});

First thing the imageOverlay is not working. When I wrap the layers inside the L.featureGroup it's working. but the images are overlapping one another instead of comparing when dragging the line.

Here is my codesandbox: https://codesandbox.io/s/react-leaflet-side-by-side-forked-m0ud6p

4
  • 1
    Does this answer your question? Leaflet side by side plugin is not working with imageOverlay
    – TomazicM
    Commented Feb 13, 2023 at 17:57
  • No, actually i referred the above link and tried but it didn't worked. Commented Feb 14, 2023 at 4:19
  • I checked again and discovered that this workaround worked in Leaflet 1.7, but does not work any more in Leaflet 1.9.3. I'll try to find out the cure for that.
    – TomazicM
    Commented Feb 15, 2023 at 10:03
  • Ok, Thanks @TomazicM Commented Feb 16, 2023 at 4:19

2 Answers 2

3

There are two things that have to be fixed in vanilla leaflet-side-by-side plugin to make it work with L.imageOverlay layers in Leaflet v1.9.

First thing that was necessary also in previous version of Leaflet is to replace every occurrence of this._leftLayer.getContainer() statement with this._leftLayer.getPane() statement.

Second problem that appeared with Leaflet v1.9 is that it no longer fires touchstart event when L.Browser.touch reports true but actual event was triggered with mouse down. I'm not some HTML/DOM/JS expert, but one possible solution for this is to introduce function that tests if device is actually a touch device:

function isTouch() {
  return (('ontouchstart' in window)
      || (navigator.MaxTouchPoints > 0)
      || (navigator.msMaxTouchPoints > 0));    
}

The needed plugin code fix would then be:

//    on(range, L.Browser.touch ? 'touchstart' : 'mousedown', cancelMapDrag, this)
//    on(range, L.Browser.touch ? 'touchend' : 'mouseup', uncancelMapDrag, this)
    on(range, isTouch() ? 'touchstart' : 'mousedown', cancelMapDrag, this)
    on(range, isTouch() ? 'touchend' : 'mouseup', uncancelMapDrag, this)

Since plugin code is not that big, here is then whole modified plugin (tested):

(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
(function (global){
var L = (typeof window !== "undefined" ? window['L'] : typeof global !== "undefined" ? global['L'] : null)
require('./layout.css')
require('./range.css')

var mapWasDragEnabled
var mapWasTapEnabled

// Leaflet v0.7 backwards compatibility
function on (el, types, fn, context) {
  types.split(' ').forEach(function (type) {
    L.DomEvent.on(el, type, fn, context)
  })
}

// Leaflet v0.7 backwards compatibility
function off (el, types, fn, context) {
  types.split(' ').forEach(function (type) {
    L.DomEvent.off(el, type, fn, context)
  })
}

function getRangeEvent (rangeInput) {
  return 'oninput' in rangeInput ? 'input' : 'change'
}

function cancelMapDrag () {
  mapWasDragEnabled = this._map.dragging.enabled()
  mapWasTapEnabled = this._map.tap && this._map.tap.enabled()
  this._map.dragging.disable()
  this._map.tap && this._map.tap.disable()
}

function uncancelMapDrag (e) {
  this._refocusOnMap(e)
  if (mapWasDragEnabled) {
    this._map.dragging.enable()
  }
  if (mapWasTapEnabled) {
    this._map.tap.enable()
  }
}

// convert arg to an array - returns empty array if arg is undefined
function asArray (arg) {
  return (arg === 'undefined') ? [] : Array.isArray(arg) ? arg : [arg]
}

function noop () {}

// start fix for Leaflet 1.9 -----------------------
function isTouch() {
  return (('ontouchstart' in window)
      || (navigator.MaxTouchPoints > 0)
      || (navigator.msMaxTouchPoints > 0));    
}
// end fix for Leaflet 1.9 -----------------------

L.Control.SideBySide = L.Control.extend({
  options: {
    thumbSize: 42,
    padding: 0
  },

  initialize: function (leftLayers, rightLayers, options) {
    this.setLeftLayers(leftLayers)
    this.setRightLayers(rightLayers)
    L.setOptions(this, options)
  },

  getPosition: function () {
    var rangeValue = this._range.value
    var offset = (0.5 - rangeValue) * (2 * this.options.padding + this.options.thumbSize)
    return this._map.getSize().x * rangeValue + offset
  },

  setPosition: noop,

  includes: L.Evented.prototype || L.Mixin.Events,

  addTo: function (map) {
    this.remove()
    this._map = map

    var container = this._container = L.DomUtil.create('div', 'leaflet-sbs', map._controlContainer)

    this._divider = L.DomUtil.create('div', 'leaflet-sbs-divider', container)
    var range = this._range = L.DomUtil.create('input', 'leaflet-sbs-range', container)
    range.type = 'range'
    range.min = 0
    range.max = 1
    range.step = 'any'
    range.value = 0.5
    range.style.paddingLeft = range.style.paddingRight = this.options.padding + 'px'
    this._addEvents()
    this._updateLayers()
    return this
  },

  remove: function () {
    if (!this._map) {
      return this
    }
    if (this._leftLayer) {
// start fix for Leaflet 1.9 -----------------------
//      this._leftLayer.getContainer().style.clip = ''
      this._leftLayer.getPane().style.clip = ''
// end fix for Leaflet 1.9 -----------------------
    }
    if (this._rightLayer) {
// start fix for Leaflet 1.9 -----------------------
//      this._rightLayer.getContainer().style.clip = ''
      this._rightLayer.getPane().style.clip = ''
// end fix for Leaflet 1.9 -----------------------
    }
    this._removeEvents()
    L.DomUtil.remove(this._container)

    this._map = null

    return this
  },

  setLeftLayers: function (leftLayers) {
    this._leftLayers = asArray(leftLayers)
    this._updateLayers()
    return this
  },

  setRightLayers: function (rightLayers) {
    this._rightLayers = asArray(rightLayers)
    this._updateLayers()
    return this
  },

  _updateClip: function () {
    var map = this._map
    var nw = map.containerPointToLayerPoint([0, 0])
    var se = map.containerPointToLayerPoint(map.getSize())
    var clipX = nw.x + this.getPosition()
    var dividerX = this.getPosition()

    this._divider.style.left = dividerX + 'px'
    this.fire('dividermove', {x: dividerX})
    var clipLeft = 'rect(' + [nw.y, clipX, se.y, nw.x].join('px,') + 'px)'
    var clipRight = 'rect(' + [nw.y, se.x, se.y, clipX].join('px,') + 'px)'
    if (this._leftLayer) {
// start fix for Leaflet 1.9 -----------------------
//      this._leftLayer.getContainer().style.clip = clipLeft
      this._leftLayer.getPane().style.clip = clipLeft
    }
    if (this._rightLayer) {
// start fix for Leaflet 1.9 -----------------------
//      this._rightLayer.getContainer().style.clip = clipRight
      this._rightLayer.getPane().style.clip = clipRight
// end fix for Leaflet 1.9 -----------------------
    }
  },

  _updateLayers: function () {
    if (!this._map) {
      return this
    }
    var prevLeft = this._leftLayer
    var prevRight = this._rightLayer
    this._leftLayer = this._rightLayer = null
    this._leftLayers.forEach(function (layer) {
      if (this._map.hasLayer(layer)) {
        this._leftLayer = layer
      }
    }, this)
    this._rightLayers.forEach(function (layer) {
      if (this._map.hasLayer(layer)) {
        this._rightLayer = layer
      }
    }, this)
    if (prevLeft !== this._leftLayer) {
      prevLeft && this.fire('leftlayerremove', {layer: prevLeft})
      this._leftLayer && this.fire('leftlayeradd', {layer: this._leftLayer})
    }
    if (prevRight !== this._rightLayer) {
      prevRight && this.fire('rightlayerremove', {layer: prevRight})
      this._rightLayer && this.fire('rightlayeradd', {layer: this._rightLayer})
    }
    this._updateClip()
  },

  _addEvents: function () {
    var range = this._range
    var map = this._map
    if (!map || !range) return
    map.on('move', this._updateClip, this)
    map.on('layeradd layerremove', this._updateLayers, this)
    on(range, getRangeEvent(range), this._updateClip, this)
// start fix for Leaflet 1.9 -----------------------
//    on(range, L.Browser.touch ? 'touchstart' : 'mousedown', cancelMapDrag, this)
//    on(range, L.Browser.touch ? 'touchend' : 'mouseup', uncancelMapDrag, this)
    on(range, isTouch() ? 'touchstart' : 'mousedown', cancelMapDrag, this)
    on(range, isTouch() ? 'touchend' : 'mouseup', uncancelMapDrag, this)
// end fix for Leaflet 1.9 -----------------------
  },

  _removeEvents: function () {
    var range = this._range
    var map = this._map
    if (range) {
      off(range, getRangeEvent(range), this._updateClip, this)
      off(range, L.Browser.touch ? 'touchstart' : 'mousedown', cancelMapDrag, this)
      off(range, L.Browser.touch ? 'touchend' : 'mouseup', uncancelMapDrag, this)
    }
    if (map) {
      map.off('layeradd layerremove', this._updateLayers, this)
      map.off('move', this._updateClip, this)
    }
  }
})

L.control.sideBySide = function (leftLayers, rightLayers, options) {
  return new L.Control.SideBySide(leftLayers, rightLayers, options)
}

module.exports = L.Control.SideBySide

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./layout.css":2,"./range.css":4}],2:[function(require,module,exports){
var inject = require('./node_modules/cssify');
var css = ".leaflet-sbs-range {\r\n    position: absolute;\r\n    top: 50%;\r\n    width: 100%;\r\n    z-index: 999;\r\n}\r\n.leaflet-sbs-divider {\r\n    position: absolute;\r\n    top: 0;\r\n    bottom: 0;\r\n    left: 50%;\r\n    margin-left: -2px;\r\n    width: 4px;\r\n    background-color: #fff;\r\n    pointer-events: none;\r\n    z-index: 999;\r\n}\r\n";
inject(css, undefined, '_i6aomd');
module.exports = css;

},{"./node_modules/cssify":3}],3:[function(require,module,exports){
'use strict'

function injectStyleTag (document, fileName, cb) {
  var style = document.getElementById(fileName)

  if (style) {
    cb(style)
  } else {
    var head = document.getElementsByTagName('head')[0]

    style = document.createElement('style')
    if (fileName != null) style.id = fileName
    cb(style)
    head.appendChild(style)
  }

  return style
}

module.exports = function (css, customDocument, fileName) {
  var doc = customDocument || document
  /* istanbul ignore if: not supported by Electron */
  if (doc.createStyleSheet) {
    var sheet = doc.createStyleSheet()
    sheet.cssText = css
    return sheet.ownerNode
  } else {
    return injectStyleTag(doc, fileName, function (style) {
      /* istanbul ignore if: not supported by Electron */
      if (style.styleSheet) {
        style.styleSheet.cssText = css
      } else {
        style.innerHTML = css
      }
    })
  }
}

module.exports.byUrl = function (url) {
  /* istanbul ignore if: not supported by Electron */
  if (document.createStyleSheet) {
    return document.createStyleSheet(url).ownerNode
  } else {
    var head = document.getElementsByTagName('head')[0]
    var link = document.createElement('link')

    link.rel = 'stylesheet'
    link.href = url

    head.appendChild(link)
    return link
  }
}

},{}],4:[function(require,module,exports){
var inject = require('./node_modules/cssify');
var css = ".leaflet-sbs-range {\r\n    -webkit-appearance: none;\r\n    display: inline-block!important;\r\n    vertical-align: middle;\r\n    height: 0;\r\n    padding: 0;\r\n    margin: 0;\r\n    border: 0;\r\n    background: rgba(0, 0, 0, 0.25);\r\n    min-width: 100px;\r\n    cursor: pointer;\r\n    pointer-events: none;\r\n    z-index: 999;\r\n}\r\n.leaflet-sbs-range::-ms-fill-upper {\r\n    background: transparent;\r\n}\r\n.leaflet-sbs-range::-ms-fill-lower {\r\n    background: rgba(255, 255, 255, 0.25);\r\n}\r\n/* Browser thingies */\r\n\r\n.leaflet-sbs-range::-moz-range-track {\r\n    opacity: 0;\r\n}\r\n.leaflet-sbs-range::-ms-track {\r\n    opacity: 0;\r\n}\r\n.leaflet-sbs-range::-ms-tooltip {\r\n    display: none;\r\n}\r\n/* For whatever reason, these need to be defined\r\n * on their own so dont group them */\r\n\r\n.leaflet-sbs-range::-webkit-slider-thumb {\r\n    -webkit-appearance: none;\r\n    margin: 0;\r\n    padding: 0;\r\n    background: #fff;\r\n    height: 40px;\r\n    width: 40px;\r\n    border-radius: 20px;\r\n    cursor: ew-resize;\r\n    pointer-events: auto;\r\n    border: 1px solid #ddd;\r\n    background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAMAAAC5zwKfAAAABlBMVEV9fX3///+Kct39AAAAAnRSTlP/AOW3MEoAAAA9SURBVFjD7dehDQAwDANBZ/+l2wmKoiqR7pHRcaeaCxAIBAL/g7k9JxAIBAKBQCAQCAQC14H+MhAIBE4CD3fOFvGVBzhZAAAAAElFTkSuQmCC\");\r\n    background-position: 50% 50%;\r\n    background-repeat: no-repeat;\r\n    background-size: 40px 40px;\r\n}\r\n.leaflet-sbs-range::-ms-thumb {\r\n    margin: 0;\r\n    padding: 0;\r\n    background: #fff;\r\n    height: 40px;\r\n    width: 40px;\r\n    border-radius: 20px;\r\n    cursor: ew-resize;\r\n    pointer-events: auto;\r\n    border: 1px solid #ddd;\r\n    background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAMAAAC5zwKfAAAABlBMVEV9fX3///+Kct39AAAAAnRSTlP/AOW3MEoAAAA9SURBVFjD7dehDQAwDANBZ/+l2wmKoiqR7pHRcaeaCxAIBAL/g7k9JxAIBAKBQCAQCAQC14H+MhAIBE4CD3fOFvGVBzhZAAAAAElFTkSuQmCC\");\r\n    background-position: 50% 50%;\r\n    background-repeat: no-repeat;\r\n    background-size: 40px 40px;\r\n}\r\n.leaflet-sbs-range::-moz-range-thumb {\r\n    padding: 0;\r\n    right: 0    ;\r\n    background: #fff;\r\n    height: 40px;\r\n    width: 40px;\r\n    border-radius: 20px;\r\n    cursor: ew-resize;\r\n    pointer-events: auto;\r\n    border: 1px solid #ddd;\r\n    background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAMAAAC5zwKfAAAABlBMVEV9fX3///+Kct39AAAAAnRSTlP/AOW3MEoAAAA9SURBVFjD7dehDQAwDANBZ/+l2wmKoiqR7pHRcaeaCxAIBAL/g7k9JxAIBAKBQCAQCAQC14H+MhAIBE4CD3fOFvGVBzhZAAAAAElFTkSuQmCC\");\r\n    background-position: 50% 50%;\r\n    background-repeat: no-repeat;\r\n    background-size: 40px 40px;\r\n}\r\n.leaflet-sbs-range:disabled::-moz-range-thumb {\r\n    cursor: default;\r\n}\r\n.leaflet-sbs-range:disabled::-ms-thumb {\r\n    cursor: default;\r\n}\r\n.leaflet-sbs-range:disabled::-webkit-slider-thumb {\r\n    cursor: default;\r\n}\r\n.leaflet-sbs-range:disabled {\r\n    cursor: default;\r\n}\r\n.leaflet-sbs-range:focus {\r\n    outline: none!important;\r\n}\r\n.leaflet-sbs-range::-moz-focus-outer {\r\n    border: 0;\r\n}\r\n\r\n";
inject(css, undefined, '_1tlt668');
module.exports = css;

},{"./node_modules/cssify":3}]},{},[1]);
1
  • This solution is working perfectly. Thanks @TomazicM Commented Feb 16, 2023 at 4:21
0

Resonating with @TomazicM, replacing every occurrence of this._leftLayer.getContainer() statement with this._leftLayer.getPane() statement did fix my issue. In my case I've tried accessing two raster layers from ArcGIS Rest Service to visualize changes using leaflet-side-by-side. The updated leaflet-side-by-side.js code by @TomazicM works perfectly fine. Here is the index.html code that I have used.

<!DOCTYPE html>
<html>
<head>
    <meta charset=utf-8 />
    <meta name='viewport'
        content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui' />
    <title>Leaflet Side-by-side</title>
    <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
    <script src="https://unpkg.com/[email protected]/dist/esri-leaflet.js"
        integrity="sha512-G4+fuKc1B96F0sUG7eKtgiJr0JM3iQC4bfc8WtYvmm7msHES0Hst2mLWASPb8zZk91tqMG86AjP0tgXIEFPPUA=="
        crossorigin=""></script>

    <script src="leaflet-side-by-side.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />

    <style>
        .rightPopup {
            z-index: 750;
        }
        body {
            margin: 0;
            padding: 0;
        }
        #map {
            position: absolute;
            top: 0;
            bottom: 0;
            width: 100%;
        }
    </style>
</head>

<body>
    <div id="map"></div>

    <script>
        var map = L.map('map').setView([27.505, 90.09], 9);
        map.createPane('left');
        map.createPane('right');


        var layer1 = L.esri.dynamicMapLayer({
            url: 'https://your-rest-service.com/arcgis/rest/services/YourMapService/MapServer',
            layers: [id],
            pane: 'left'
        }).addTo(map);

        var layer2 = L.esri.dynamicMapLayer({
            url: 'https://your-rest-service.com/arcgis/rest/services/YourMapService/MapServer',
            layers: [id],
            pane: 'right'
        }).addTo(map);

        // Always add sideBySideControl to the map
        var sideBySideControl = L.control.sideBySide(layer1, layer2).addTo(map);

    </script>

</body>

</html>
New contributor
Nimesh Bhandari is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.