Can Leaflet jump to new world copy on zoom instead of just on panning?

Viewed 710

The markers will appear on the map if I pan the map, but if I zoom out and then zoom in on a copy of the map without the markers, they will not appear until I pan again.

Is it possible to change this so that zooming in and out will cause the markers to recalculate on the map?

L.map('map', {
  'center': [0, 0],
  'zoom': 0,
  'worldCopyJump': true,
  'layers': [
    L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      'attribution': 'Map data © OpenStreetMap contributors'
    }),
    L.marker([0, -135]),
    L.marker([0, -90]),
    L.marker([0, -45]),
    L.marker([0, 0]),
    L.marker([0, 45]),
    L.marker([0, 90]),
    L.marker([0, 135])
  ]
});
body {
    margin: 0;
}
html, body, #map {
    height: 100%
}
<link href="https://unpkg.com/leaflet/dist/leaflet.css" rel="stylesheet"/>
<script src="https://unpkg.com/leaflet/dist/leaflet-src.js"></script>
<div id="map"></div>

http://embed.plnkr.co/mWKc4M/

1 Answers

The markers will appear on the map if I pan the map

Specifically, this behaviour happens only when dragging the map (and not when panning the map via any other method, e.g. using keyboard shortcuts). This is because, internally, the worldCopyJump functionality is defined inside the Drag handler at src/map/handler/Map.Drag.js:

// TODO refactor, move to CRS
// @option worldCopyJump: Boolean = false
// With this option enabled, the map tracks when you pan to another "copy"
// of the world and seamlessly jumps to the original one so that all overlays
// like markers and vector layers are still visible.
worldCopyJump: false,

(Do note that Leaflet has an explanation of what map handlers are and how they work)

As the code stands now, the worldCopyJump functionality affects only the dragging handler, and works by resetting the drag offset (instead of the map center) every time the map dragging handler is about to be updated:

if (map.options.worldCopyJump) {
    this._draggable.on('predrag', this._onPreDragWrap, this);
    map.on('zoomend', this._onZoomEnd, this);

    map.whenReady(this._onZoomEnd, this);
}
/* snip */
_onPreDragWrap: function () {
    // TODO refactor to be able to adjust map pane position after zoom
    var worldWidth = this._worldWidth,
        halfWidth = Math.round(worldWidth / 2),
        dx = this._initialWorldOffset,
        x = this._draggable._newPos.x,
        newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
        newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
        newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;

    this._draggable._absPos = this._draggable._newPos.clone();
    this._draggable._newPos.x = newX;
},

So, what to do? An option is to leverage wrapLatLng to reset the map center on every zoomend event, e.g.:

map.on('zoomend', function(ev){
    map.panTo(map.wrapLatLng(map.getCenter()), {animate: false})
});

That should just work. See a working demo.


As an alternative, consider using https://gitlab.com/IvanSanchez/Leaflet.RepeatedMarkers , which will create copies of each marker every 360° of longitude.

Related