8

I am using Leaflet with scalajs-leaflet facade on Binding.scala, and the map initializes/appears incorrectly.

In order to reproduce the issue, i have prepared a lihaoyi/workbench page similar to that in scalajs-leaflet.

First, download the forked scalajs-leaflet from https://github.com/mcku/scalajs-leaflet

Run sbt in scalajs-leaflet directory. Enter ~ example/fastOptJS in sbt. Now, a web server started at port 12345.

Open http://localhost:12345/example/target/scala-2.12/classes/leaflet2binding-dev.html in a browser

The problem is the map container appears but the content (tiles, etc) is not correct. Map becomes fine after a small resize on window, which triggers _onResize handler of leaflet.

The container is in the Leaflet2Binding.scala file and has its size already specified prior to initialization:

val mapElement = <div id="mapid" style="width: 1000px; height: 600px;
                       position: relative; outline: currentcolor none medium;"
                    class="leaflet-container leaflet-touch leaflet-fade-anim 
                    leaflet-grab leaflet-touch-drag leaflet-touch-zoom"
                    data:tabindex="0"></div>.asInstanceOf[HTMLElement]

It is possible to insert a line lmap.invalidateSize(true) in the following line before returning the element https://github.com/mcku/scalajs-leaflet/blob/83b770bc76de450567ababf6c7d2af0700dd58c9/example/src/main/scala/example/Leaflet2Binding.scala#L39, but did not help in this case. Namely here:

@dom def renderMap = {
  val mapElement = ... (same element as above)

  .. some other initializations ..

  lmap.invalidateSize(true) // true means, use animation

  println("mapElement._leaflet_id " +mapElement.asInstanceOf[js.Dynamic]._leaflet_id) // prints non-null value, makes me think the container is initialized

  mapElement
}

Any ideas? This is binding.scala specific, but it may be a leaflet issue as well. map before resize after resize

EDIT Possible workaround It looks like, the map element has its clientWidth property not available during the process. Which is understandable as the document is not "ready" yet. However, the css style.width is available and could be defined in px. In that case it is possible to patch leaflet to take css style width into account during computation.

it works if the style width is specified in px.

diff --git a/src/map/Map.js b/src/map/Map.js
index b94dd443..6544d7b7 100644
--- a/src/map/Map.js
+++ b/src/map/Map.js
@@ -903,8 +903,9 @@ export var Map = Evented.extend({
        getSize: function () {
                if (!this._size || this._sizeChanged) {
                    this._size = new Point(
-                               this._container.clientWidth || 0,
-                               this._container.clientHeight || 0);
+
+                               this._container.clientWidth || parseInt(this._container.style.width.replace("px",""),10) || 0,^M
+                               this._container.clientHeight || parseInt(this._container.style.height.replace("px",""),10) || 0);;^M

                        this._sizeChanged = false;
                }
pme
  • 14,156
  • 3
  • 52
  • 95
mcku
  • 1,351
  • 12
  • 23
  • 4
    This is a known and well-documented issue with Leaflet. If the map container `div` doesn't have a defined size at the point that the map initialises, the tiles don't load. If it's not possible to give the map container a size on page load due to something dynamic in your config, you can work round it by calling `map.invalidateSize()` after the container is initialised. – peeebeee Dec 21 '18 at 07:14
  • Possible duplicate of [Why isn't my map completely showing?](https://stackoverflow.com/questions/41742326/why-isnt-my-map-completely-showing) – peeebeee Dec 21 '18 at 07:15
  • @peeebeee thank you for the comments. I have updated the question. Yes, it is similar to the others, but not a duplicate case (please see the edits). – mcku Dec 21 '18 at 08:26
  • 2
    `invalidateSize()`, not `invalidateResize()`. – peeebeee Dec 21 '18 at 08:45
  • Yes, indeed `invalidateSize()` does not help. – mcku Dec 21 '18 at 08:49
  • OK. I don't know scala, but... at the point that the `invalidateSize()` executes, is the map container fully initialised in the DOM? – peeebeee Dec 21 '18 at 09:03
  • I can get the `_leaflet_id` from the container. See the `println` in the edit. – mcku Dec 21 '18 at 09:18

2 Answers2

7

Maybe lmap.invalidateSize(true) is called too early (DOM is not ready or repainted).

Make sure this does not happen. To prevent that I wrap this code like:

setTimeout(function () {
   mapid.invalidateSize(true);
}, 100);

This must be done after every DOM repaint.

Jasom Dotnet
  • 1,225
  • 12
  • 17
pme
  • 14,156
  • 3
  • 52
  • 95
  • There should be no need to explicitly wait for the dom to settle and the id of the element is queryable (because the binding does that for me already). Also because I am passing the element to L.map() function as argument, not the id of the element. – mcku Dec 21 '18 at 11:24
0

People that are still facing this issue for react-leaflet, making the component only render when zoom and center are initiated, and removed when the Map is not displaying:

useEffect(() => {
 setCenter([50.5, 30.5]);
 setZoom(14);
}, []);

And then in the JSX

{zoom && center.length > 0 && (
        <div className={styles.map}>
          <Map center={center} zoom={zoom} />
        </div>
 )}
Sharky
  • 421
  • 6
  • 5