question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Map breaks when created dynamically in combination with disabled worldCopyJump

See original GitHub issue

I’ve come across a strange issue when creating the map container in JS, appending it to a wrapper and setting the map size at a later time. This only occurs when worldCopyJump is disabled, however.

https://jsfiddle.net/2w6apqxy/

The map tiles are partially broken and the map center is at an incorrect position. When appending the container before L.map() (https://jsfiddle.net/2w6apqxy/1/) or after L.tileLayer() (https://jsfiddle.net/2w6apqxy/2/), the issue isn’t present. When enabling worldCopyJump, the issue isn’t present either, regardless of when the container is appended to the wrap.

Is this a bug related to how the map dimensions are determined when worldCopyJump is disabled vs. enabled, or is this expected behavior?

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:6 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
ghybscommented, Aug 22, 2018

Hi @alex2wong,

Your screenshot and description look like a totally different issue from what OP described.

I suspect your case to be similar to: https://stackoverflow.com/questions/38835758/leaflet-drawing-tiles-disjointly/38836996#38836996

1reaction
ghybscommented, May 22, 2018

Hi,

Hum looks like a funny edge case indeed.

What happens is that the map._sizeChanged remains at true from L.map initialization.

Since center and zoom options are defined in L.map, the map calls setView method, which will use map.getSize (in _resetView). But a little bit later the map initialization (re)sets the flag map._sizeChanged = true.

https://github.com/Leaflet/Leaflet/blob/ba6f97fff8647e724e4dfe66d2ed7da11f908989/src/map/Map.js#L145-L154

Therefore on next map.getSize() call, it will re-read the map viewport size.

https://github.com/Leaflet/Leaflet/blob/ba6f97fff8647e724e4dfe66d2ed7da11f908989/src/map/Map.js#L852-L853

This interferes with how invalidateSize works, which needs to read the old size first. But since the _sizeChanged flag is still true, the first (“old”) reading already uses the new size. This makes the view reset fail.

https://github.com/Leaflet/Leaflet/blob/ba6f97fff8647e724e4dfe66d2ed7da11f908989/src/map/Map.js#L533-L540

This situation occurs when the map view is set at initialization, then viewport size changes before next call to map.getSize() (in OP’s case when appending the map container => width already changes, then adding a Tile Layer, explicitly calling map.invalidateSize(), etc.).

It does not occur if specifying the option worldCopyJump, because the Map.Drag handler adds an init hook that is called after the map._sizeChanged = true flag, and if the option is true, it calls map.getSize() already, therefore before the map viewport size changes.

Therefore the fix seems to be to perform the private variables initialization before setting the map view: https://jsfiddle.net/2w6apqxy/3/

L.Map.include({
  initialize: function(id, options) { // (HTMLElement or String, Object)
    options = L.Util.setOptions(this, options);

    ////////////////////////////////////////////
    // Initialize private variables first,
    // so that we avoid inconsistent state.
    ////////////////////////////////////////////
    this._handlers = [];
    this._layers = {};
    this._zoomBoundLayers = {};
    this._sizeChanged = true;
    ////////////////////////////////////////////

    this._initContainer(id);
    this._initLayout();

    // hack for https://github.com/Leaflet/Leaflet/issues/1980
    this._onResize = L.Util.bind(this._onResize, this);

    this._initEvents();

    if (options.maxBounds) {
      this.setMaxBounds(options.maxBounds);
    }

    if (options.zoom !== undefined) {
      this._zoom = this._limitZoom(options.zoom);
    }

    if (options.center && options.zoom !== undefined) {
      this.setView(L.latLng(options.center), options.zoom, {
        reset: true
      });
    }
    
    ////////////////////////////////////////////
    // Instead of initializing private variables
    // in the middle of the process…
    ////////////////////////////////////////////
    /*this._handlers = [];
    this._layers = {};
    this._zoomBoundLayers = {};
    this._sizeChanged = true;*/
    ////////////////////////////////////////////

    this.callInitHooks();

    // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
    this._zoomAnimated = L.DomUtil.TRANSITION && L.Browser.any3d && !L.Browser.mobileOpera &&
      this.options.zoomAnimation;

    // zoom transitions run with the same duration for all layers, so if one of transitionend events
    // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
    if (this._zoomAnimated) {
      this._createAnimProxy();
      L.DomEvent.on(this._proxy, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
    }

    this._addLayers(this.options.layers);
  },
});
Read more comments on GitHub >

github_iconTop Results From Across the Web

how to change worldCopyJump value dynamically in leaflet?
You can access the map handlers as map properties, then enable and disable them. As the worldCopyJump option is evaluated when enabling the ......
Read more >
Documentation - a JavaScript library for interactive maps
Checks if the map container size changed and updates the map if so — call it after you've changed the map size dynamically,...
Read more >
Leaflet's worldCopyJump breaks map, zooming fixes it up ...
I had my map working with worldCopyJump set to true , but then it stopped working. I'm not sure exactly what broke it...
Read more >
leaflet - UNPKG
Object): Object\r\n// Compatibility polyfill for [Object.create](https:// ... changed the map size dynamically, also animating\r\n\t// pan by default.
Read more >
leaflet | Yarn - Package Manager
JavaScript library for mobile-friendly interactive maps. gis, map. readme. Leaflet was created 11 years ago by Volodymyr Agafonkin, a Ukrainian citizen ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found