GithubHelp home page GithubHelp logo

digidem / leaflet-side-by-side Goto Github PK

View Code? Open in Web Editor NEW
348.0 18.0 109.0 4.14 MB

A Leaflet control to add a split screen to compare two map overlays

Home Page: http://lab.digital-democracy.org/leaflet-side-by-side/

License: MIT License

HTML 6.70% JavaScript 81.36% CSS 11.95%

leaflet-side-by-side's Introduction

leaflet-side-by-side

A Leaflet control to add a split screen to compare two map overlays.

screencast example

L.control.sideBySide(leftLayer[s], rightLayer[s])

Creates a new Leaflet Control for comparing two layers or collections of layers. It does not add the layers to the map - you need to do that manually. Extends L.Control but setPosition() and getPosition are noop because the position is always the same - it does not make sense for this control to be in the corner like other Leaflet controls.

Parameters

parameter type description
leftLayers L.Layer|array A Leaflet Layer or array of layers to show on the left side of the map. Any layer added to the map that is in this array will be shown on the left
rightLayers L.Layer|array A Leaflet Layer or array of layers to show on the right side of the map. Any layer added to the map that is in this array will be shown on the right. These should not be the same as any layers in leftLayers
options Object Options
options.padding Number Padding between slider min/max and the edge of the screen in pixels. Defaults to 44 - the width of the slider thumb

Events

Subscribe to events using these methods

Event Data Description
leftlayeradd LayerEvent Fired when a layer is added to the left-hand-side pane
leftlayerremove LayerEvent Fired when a layer is removed from the left-hand-side pane
rightlayeradd LayerEvent Fired when a layer is added to the right-hand-side pane
rightlayerremove LayerEvent You guessed it... fired when a layer is removed from the right-hand-side pane
dividermove {x: Number} Fired when the divider is moved. Returns an event object with the property x = the pixels of the divider from the left side of the map container.

Methods

Method Returns Description
setLeftLayers this Set the layer(s) for the left side
setRightLayers this Set the layer(s) for the right side

Usage

Add the script to the top of your page (css is included in the javascript):

<script src="leaflet-side-by-side.js"></script>

Or if you are using browserify:

var sideBySide = require('leaflet-side-by-side')

Then create a map, add two layers to it, and create the SideBySide control and add it to the map:

var map = L.map('map').setView([51.505, -0.09], 13);

var myLayer1 = L.tileLayer(...).addTo(map);

var myLayer2 = L.tileLayer(...).addTo(map)

L.control.sideBySide(myLayer1, myLayer2).addTo(map);

Example

Live Example see source

Limitations

  • The divider is not movable with IE.
  • Probably won't work in IE8, but what does?

License

MIT

leaflet-side-by-side's People

Contributors

bertspaan avatar danielbarela avatar ghybs avatar gmaclennan avatar m-mohr avatar ukonhattu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

leaflet-side-by-side's Issues

Support for vector tiles

I'm using a vector tile, but unfortunately can't get the slider to work the same on it as it would for a regular tile:

var config = {
    url: "http://d25uarhxywzl1j.cloudfront.net/v0.1/{z}/{x}/{y}.mvt"
    };
    var mapillarySource = new L.TileLayer.MVTSource(config);
    mapillarySource.setStyle(mlystyle);
    mapillarySource.addTo(map);

Then trying:

L.control.sideBySide(gsvLayer, mapillarySource).addTo(map);

But this works only for gsvLayer which is a normal L.TileLayer, while mapillarySource is displayed regardless of slider.

Npm and package.json versions are different

The last version in npm is 2.1.0, but the one in the package.json is 2.0.0.

I've created a MR increasing the package.json version to 2.0.1, but I'm not sure if it should be 2.1.1.

The example code is not working in Chrome or Edge, windows 10

Hi, I have copy and pasted your code into a local html file, and only one of the layers is displayed in the page. I am using Chrome, in Windows 10. I have also tried Microsoft Edge, even though you say it would not work, anyway... Any suggestions?

WMS Layer

Hello

Will it works with WMS layers?

Thanks

Dragging button isn't in the middle of the map

1653299024(1)

1653299041(1)
and my code is:

<script> import { onMounted, reactive } from "@vue/runtime-core"; import DrawTool from "./components/drawTool.vue"; // import {} from "leaflet-splitmap";

export default {
name: "App",
components: {
DrawTool,
},
setup() {
// console.log(a);
let L = window.L;
let map = reactive({});
let data = reactive({ myMap: null });
onMounted(() => {
map = L.map("map", {
// 初始化地图信息
crs: L.CRS.EPSG4326,
center: { lat: 22.791758946936532, lng: 113.52616661006695 },
maxZoom: 18,
zoom: 14,
});
data.myMap = map;
require("leaflet-side-by-side");
require("leaflet-splitmap");
var myLayer2 = L.supermap.tiandituTileLayer({
layerType: "vec",
// url:'http://t0.tianditu.gov.cn/cva_c/wmts?',
key: "49d42799e2f10cf2d3c1d24c02cdb71f",
// isLabel:true
}).addTo(map);

  var myLayer1 = L.supermap.tiandituTileLayer({
      layerType: "img",
      key: "49d42799e2f10cf2d3c1d24c02cdb71f",
      // isLabel: true,
    }).addTo(map);
    
    L.control.sideBySide(myLayer2,myLayer1).addTo(map);

  // map.on("mouseup", (e) => {
  //   let latlng = e.latlng;
  //   console.log(latlng); // {lat: 30.59, lng: 114.32}
  //   console.log(map.getZoom()); // {lat: 30.59, lng: 114.32}
  // });
});
return {
  data,
};

},
};
</script>

<style> body { margin: 0; } #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; } #map { position: absolute; left: 0px; right: 0px; width: 100%; height: 100%; } </style>

How to remove controller

Doesnt seem like the typical removeFrom(map) is supported for this plugin. Am i getting it wrong? If so, could you provide an example?

Dynamically adding layers breaks clip function

We initialize the side by side with:

let divider = L.control.sideBySide([streets], [streets2]).addTo(map);

We then try to set the left layers with:

divider.setLeftLayers([streets, layer]);

After doing so the map looks like the following image below.

image

This is due to the clip not being reflected on all layers on the left side.

The clip is being set at this line

if (this._leftLayer) { this._leftLayer.getContainer().style.clip = clipLeft } if (this._rightLayer) { this._rightLayer.getContainer().style.clip = clipRight }

In order to fix this you can update to loop thru all of the layers.

for(let layer of this._leftLayers) { layer.getContainer().style.clip = clipLeft } for(let layer of this._rightLayers) { layer.getContainer().style.clip = clipRight }

This will fix the rendering error for the layers that are not set to _leftLayer or _rightLayer.

L.Mixin.Events: this property will be removed in future releases

L.Mixin.Events will be removed in future releases
we may need a judgement in includes,
let events = null
//If Version is >= 1.2.0
if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) {
events = L.Evented.prototype;
} else {
events = L.Mixin.Events;
}

Removing layers from the control does not restore them to both sides

This is a very handy control - thanks for developing it!
I'd like to be able to add multiple layers to either both sides, the left side and/or the right side - and then shift them around between sides.
This mostly works, but when a layer is removed from the side-by-side control, its clip property is not removed, stranding it on whichever side it was on, and not updating when the control's position changes.
I have some changes which should address this, which I'll submit as a PR.

Map Dragging Issue

Hi:
I am using leaflet-side-by-side to compare two tile layers. When i drag the burger icon, the map drags instead of the dragger line.

Thanks.

Error: Map container is already initialized.

I'm having issue while rerendering the map with useEffect

  const [baseViewCoords, setBaseViewCoords] = useState([37.715, 44.8611]);

  const searchApiHandler = () => {
  // some code that will fetch new values for baseViewCoords and set to the state
  };

  useEffect(() => {

    var map = L.map('map').setView(baseViewCoords, 13);
[](url)
    var osmLayer = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap<\/a> contributors'
    }).addTo(map);

    var stamenLayer = L.tileLayer('https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.png', {
        attribution:
            'Map tiles by <a href="http://stamen.com">Stamen Design<\/a>, ' +
            '<a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0<\/a> &mdash; ' +
            'Map data {attribution.OpenStreetMap}',
        minZoom: 1,
        maxZoom: 16
    }).addTo(map)

    L.control.sideBySide(stamenLayer, osmLayer).addTo(map);
  }, [baseViewCoords]);

return (
     <div id="map" />
)

I'm trying to change the position of map and rerender it after fetching api but shows the in attached files

console-error
chrome-error

Swapping tile layers

How do I change the layers showing in each pane?

This is what I've tried, but it doesn't change the layers.

sideBySide = L.control.sideBySide(leftTileLayer, rightTileLayer, { thumbSize: 25, padding: 70 });
sideBySide.addTo(map);
sideBySide.setLeftLayers(leftTileLayer2);
sideBySide.setRightLayers(rightTileLayer2);

Dragging not possible anymore on non-touch devices

Dragging of split slider is not possible (anymore) due to an incorrect assumption of the L.Browser module.

`- on(range, L.Browser.touch ? 'touchstart' : 'mousedown', cancelMapDrag, this)

  • on(range, L.Browser.touch ? 'touchend' : 'mouseup', uncancelMapDrag, this)
    `

Tested with Google Chrome 105.0.5195.102 and version earlier.
Android/iOS is still working, only desktop browsers are affected.

Background;:

L.Browser.touch stays true even if you are using a non-touch device. It would be better to rely on global window variable like other leaflet plugins.

Not support geojson layer

I insert geojson layers on leaflets. When I then try to bring them to the plugin it doesn't work, because the layer doesn't contain the getContainer() property.

Error: this._leftLayer.getContainer is not a function

Dragging the map

When the side by side control is first loaded it is possible to drag the map, however, once the slider has been moved you can no longer drag the map.

Can somebody update this?

Live example doesn't work.
Example code when fixed still doesn't work. (stamen layers are 404, js imports badly configured))
Importing the lib to my vueJS app displays 1 layer with the slider in the middle. No additional layer added by L.control.sideBySide(x, y)

Can someone take over and fix this? I couldn't find any alternative. Some forks out there are not working either.

setLeftLayers adds layers to the right pane

@gmaclennan

I've updated the lib so that getContainer is replaced by getPane (leaflet 1.9)

const sbs = L.control.sideBySide([], []).addTo(this.map);
const layer1 = L.geoJSON(MYGEOJSONOBJECT01, { style: { color: 'green', weight: 2, opacity: 1 } }).addTo(this.map);
const layer2 = L.geoJSON(MYGEOJSONOBJECT02, { style: { color: 'red', weight: 2, opacity: 1 } }).addTo(this.map);
sbs.setLeftLayers(layer1);
sbs.setRightLayers(layer2);

Both layers are on the right pane...

I've tried setting them up directly in the sideBySide() function for the same result.

EDIT: setxxxLayers individually work, but not both at the same time. So I either get left pane working with blank right pane or the other way around.

Angular and leaflet-side-by-side

Angular 15+

Getting the following error
#18 250.6 ./node_modules/leaflet-side-by-side/layout.css:1:0 - Error: Module parse failed: Unexpected token (1:0) #18 250.6 You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders #18 250.6 > .leaflet-sbs-range { #18 250.6 | position: absolute; #18 250.6 | top: 50%; #18 250.6 #18 250.6 ./node_modules/leaflet-side-by-side/range.css:1:0 - Error: Module parse failed: Unexpected token (1:0) #18 250.6 You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders #18 250.6 > .leaflet-sbs-range { #18 250.6 | -webkit-appearance: none; #18 250.6 | display: inline-block!important;

esri-leaflet 、dynamicMapLayer、L.control.sideBySide、Vue

import Vue from "vue";
import Leaflet from "leaflet";
var esri = require("esri-leaflet");
var sideBySide = require("leaflet-side-by-side");
addDynaLayerESRI(url, opacity) {
var envLayer = esri.dynamicMapLayer({
url: url,
opacity: opacity
});
envLayer.addTo(this.map);
return envLayer;
}
this.leftLayer = this.addDynaLayerESRI(this.url1, 1);
this.rightLayer = this.addDynaLayerESRI(this.url2, 1);
this.sideControl = L.control
.sideBySide([this.leftLayer], [this.rightLayer])
.addTo(this.map);

it throws exption as:
vue.esm.js?efeb:610 [Vue warn]: Error in mounted hook: "TypeError: this._leftLayer.getContainer is not a function" found in

vue.esm.js?efeb:1841 TypeError: this._leftLayer.getContainer is not a function
at NewClass._updateClip (index.js?606c:135)
at NewClass._updateLayers (index.js?606c:167)
at NewClass.addTo (index.js?606c:89)

Adding geojsons

Is there any possible way to add separate geojsons to either of the 2 layers? Because for now it seems when I add a geojson file, it automatically adds it to both layers. I've been trying to figure out a way around it, but can't seem to find one. An example of what I'm talking about is pictured below
aus

Dragging not working with Leaflet 1.9.2

With the latest Leaflet (Leaflet 1.9.2) dragging the control is not working correctly. The map itself is being dragged. There is a simple fix. Add:
L.DomEvent.disableClickPropagation(this._container);
To the addTo function (I'm calling it right before the return this). This disables propagation of click events through to the map.

Optionally, the functions cancelMapDrag and uncancelMapDrag are not serving any purpose, and they (and the event hooks to them) can be removed.

I have no idea what Leaflet version this first started manifesting with.

Swiping between Featuregroups

Would it be possible to extend this to work on featuregroups? I have two featuregroups, each with a bunch of polylines representing a 'before' and 'after' map. I'd like to use the side-by-side library to swipe between these layers, but it seems this part only works on map layers

  this._leftLayer.getContainer().style.clip = clipLeft

setLeftLayers with >1 layers

I already read and follow code like below:
var myLayer1 = L.tileLayer(...).addTo(map);
var myLayer2 = L.tileLayer(...).addTo(map);
var sideBySide = L.control.sideBySide([myLayer1], [myLayer2]).addTo(map);

So, How can I compare many layer in leftLayer like:
var myLayer1 = L.tileLayer(...).addTo(map);
var myLayer2 = L.tileLayer(...).addTo(map);
var myLayer3 = L.tileLayer(...).addTo(map);
var myLayer4 = L.tileLayer(...).addTo(map);
var sideBySide = L.control.sideBySide([myLayer1, myLayer2, myLayer3], [myLayer4]).addTo(map);

Can I compare 3 layers in left with 1 layer in right at the same time ?

Leaflet layer Control

Hello,

is there any solution to combine that plugin with leaflet Layer Control ? If I try to dynamicly change the layers for left and right Side,destroys the mapview. Much thanks

Deprecated include of L.Mixin.Events

I'm getting warnings about: Deprecated include of L.Mixin.Events: this property will be removed in future releases, please inherit from L.Evented instead. from Leaflet. It looks like this plugin is using something that's deprecated or in the process of being so. I think this is this line: includes: L.Mixin.Events,. Would be nice to have that fixed. Thank you.

Issue with adding and removing the control

When trying to use Map.removeControl() on sideBySide, I get the following error:

leaflet-src.js:7932 Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
at NewClass.removeFrom (node_modules/leaflet/dist/leaflet-src.js:7932:10)
at NewClass.removeControl (node_modules/leaflet/dist/leaflet-src.js:7963:11)

Is this a known issue?

Doesn't support the same layer on both left and right.

My UI allows the user to select which layers they want on the left and right for comparison. This can result in the user selecting the same layer for both sides, even if just temporarily while they are making selections. The side-by-side control does not support this correctly, and typically the left side will be blanked (because there is just one layer, and the right side clip is set last, so it "wins").

The following simple fix removes the clip area from the layer if the left and right layers are the same.

_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})
    if (this._leftLayer === this._rightLayer && this._leftLayer) {
      this._leftLayer.getContainer().style.clip = '';
      this._rightLayer.getContainer().style.clip = '';
    } else {
      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) {
        this._leftLayer.getContainer().style.clip = clipLeft
      }
      if (this._rightLayer) {
        this._rightLayer.getContainer().style.clip = clipRight
      }
    }
  },

Support latest version of Leaflet

As far as I can tell, this control works with Leaflet 1.0, and I'd like to use it in a project whose dependency is for version >=1.0.
However leaflet-side-by-side's package.json forces me to use 0.7.7.

Doesn't work with imageOverlay?

This is a fantastic plugin - I got it to work quickly with TileLayer, but not ImageOverlay. The slider appears and moves, but the image on both sides of the slider is simply whichever overlay is on top, and does not change as the slider is moved. Can this plugin be used with imageOverlay?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.