Vue2leaflet: vue-marker not displaying on map and map not fully rendering

Created on 1 Nov 2017  ·  44Comments  ·  Source: vue-leaflet/Vue2Leaflet


Description

Steps to Reproduce

  1. installed Vue2Leaflet via npm
  2. created a component called Simplewith the following contents (just copied the Simple.vue example and added the style import)
<template>
  <div class="simple">
    <div id="top_div" style="height: 100%">
      <v-map :zoom="zoom" :center="center" style="height: 1000px; width: 1000px">
        <v-tilelayer :url="url" :attribution="attribution"></v-tilelayer>
        <v-marker :lat-lng="marker"></v-marker>       
      </v-map>
    </div>
  </div>
</template>

<script>
 import Vue2Leaflet from 'vue2-leaflet';

export default {
  name: 'simple',
  components: {
    'v-map': Vue2Leaflet.Map,
    'v-tilelayer' :Vue2Leaflet.TileLayer,
    'v-marker': Vue2Leaflet.Marker
  },
  data () {
    return {
        zoom: 13,
        center: [47.413220, -1.219482],
        url: 'http://{s}.tile.osm.org/{z}/{x}/{y}.png',
        attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
        marker: L.latLng(47.413220, -1.219482),
    }
  }
}
</script>

<style>
 @import "~leaflet/dist/leaflet.css";
</style>

Expected Results

I expect to see the basic map with marker from the example jsfiddle.

Actual Results

The map appears as a large gray box with a small section with rendered map tiles. My problem looks identical to the one described in #81 , but I do include the leaflet css import.

leaflet

What is strange is that when I resize the window, the map loads as it should, but the marker does not show up. I see in the html that the marker is loaded, however:
leaflet2

Browsers Affected

  • [x ] Chrome
  • [x ] Firefox
  • [ ] Edge
  • [ ] Safari 9
  • [ ] Safari 8
  • [ ] IE 11

Versions

  • Leaflet: v1.2.0
  • Vue: v2.5.2
  • Vue2Leaflet: v0.0.55
Docs help wanted wontfix

Most helpful comment

I've had some similar issues. The marker image issue I solved using info from these threads:
https://github.com/PaulLeCam/react-leaflet/issues/255
https://github.com/Leaflet/Leaflet/issues/4849
https://github.com/Leaflet/Leaflet/issues/4968

Basically, sounds like webpack is jacking up the embedded image URLs in the CSS. Throwing this at the top of your main.js may help:

import L from 'leaflet';
delete L.Icon.Default.prototype._getIconUrl;

L.Icon.Default.mergeOptions({
  iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
  iconUrl: require('leaflet/dist/images/marker-icon.png'),
  shadowUrl: require('leaflet/dist/images/marker-shadow.png')
});

I've gotten to the point that everything renders after the user resizes but I can't figure out why I'm getting grey space before that...

All 44 comments

I've had some similar issues. The marker image issue I solved using info from these threads:
https://github.com/PaulLeCam/react-leaflet/issues/255
https://github.com/Leaflet/Leaflet/issues/4849
https://github.com/Leaflet/Leaflet/issues/4968

Basically, sounds like webpack is jacking up the embedded image URLs in the CSS. Throwing this at the top of your main.js may help:

import L from 'leaflet';
delete L.Icon.Default.prototype._getIconUrl;

L.Icon.Default.mergeOptions({
  iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
  iconUrl: require('leaflet/dist/images/marker-icon.png'),
  shadowUrl: require('leaflet/dist/images/marker-shadow.png')
});

I've gotten to the point that everything renders after the user resizes but I can't figure out why I'm getting grey space before that...

I ended up with a hack that helped get everything to render correctly as far as the resize issue goes.

mounted() {
      setTimeout(function() { window.dispatchEvent(new Event('resize')) }, 250);
}

This works down to 200 ms. After that the hack fails. Seems to be some sort of race condition. I tested slower bandwidths and they work fine.

@azurelogic

Both those suggestions worked, thanks for the help.

Hi @qualisign,

The icon issue is well known. Leaflet and Webpack don't play well together. You can see the issue #28 and the pull request in Leaflet to fix this.
@azurelogic give the right way to fix this issue in the meantime.

The other issue is the map tiles not loading properly.
@azurelogic seems right to force the view to be redrawn.
I can't reproduce and I would like to know how you reproduce it.
How do you set your map container size?
If you resize the map container manually, you need to call map.invalidateSize().

Mickaël

The problem for me is that I'm using it along with Vuetity, which includes flexbox-based grid components. I have some tricks to size it as a square, but even before I added that on top I was having issues. I wish I had a simple example to illustrate.

Never experienced any issue like this on my tests.
It seems to me that this issue is a problem between Leaflet and flexbox based sizing.
I should add this hack into a tips and tricks section in the Readme.

What do you think?

Micka

Can't hurt to suggest it. Like I said though, I'm using it inside of flexbox. On top of that, I'm binding the height of the map or its container to another property that's computed from the width of a column and stuff. Can't say that everything I'm doing is a good idea, but the whole site is pretty responsive. That's the difficulty with Leaflet to begin with though, it seems.

Same problem here. Vuetify / Flexbox + gray area showing up.

I gave a try to Vuetify / Flexbox.

I started a Vue project added Vuetify and used this layout.
I added Vue2Leaflet and created a map component.
Added this map component in the main area of the baseline layout and leaflet CSS as a script tag in the index.html file.
Works fine.

The issue is that if the map container size change Leaflet doesn't know about it and doesn't load tiles to cover the new size of the container.
It appends in my test when closing the left menu, the maps get bigger a part of the map is now displayed on the right-hand side but no tile is displayed => grey area.

I'm not sure what your issue is as I can't really reproduce it but I think your map container is initialized with a small size(0px ?). The size is increased later but the map doesn't adapt like in my issue so you end up having a mostly grey area.

How to fix it :
The tricky part is to detect when the size change, the only two options I found are:

  • Wait for Resize Observer to be implemented in browsers and use it. At the moment no current version of browser implements it. It will work with Chrome 64.

    • Use a library called CSS Element Queries to detect changes using the ResizeSensor JS Class, debounce the events, and use Leaflet invalidateSize to force leaflet to adapt to the current size.

The second seems to be the only viable choice.

What do you guys think?

In the console if you type:

$('img.leaflet-marker-icon.leaflet-zoom-animated.leaflet-interactive').src

you might get something like this :

"data:image/png;base64,iVBORw0K[ ...................]UVORK5CYII=")marker-icon-2x.png"

you will notice that the extra double quotes are screwing up the get request.
try going into the DOM and removing this porting of the string and see if a marker appears.

")marker-icon-2x.png

leave the last double quote

As for the issue with sizing issue in Vuetify:
add a class on top of the v-container element :

.i {
height: 100%;
margin: 0;
padding: 0;
width: 100%;
z-index: 0;
}

then go into the vuetify.css and rip out all of the media queries for the .container class. I think its on lines 9301 to 9313.

Apologies for the question, but where in the file system do I put the "leaflet.css" to include it with
@import "~leaflet/dist/leaflet.css";

I've copied leaflet.css all over my disk, to no avail, and found no documentation of @import and were webpack looks for stuff.

It should be added in your ~/projectName/src/main.js file when you instantiate your Vue object.
I'm also using Vuetify but my main.js file looks like this:

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.css'
import "leaflet/dist/leaflet.css"
import "vue-d3-network/dist/vue-d3-network.css"




Vue.use(Vuetify, { theme: {
  primary: '#FF6E40',
  secondary: '#FF9E80',
  accent: '#26A69A',
  error: '#FF3D00',
  warning: '#EA80FC',
  info: '#29B6F6',
  success: '#64DD17'
}})

Vue.config.productionTip = false


new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

Many thanks, so simple ...

Hi,

I'm having similar issues as @azurelogic (Vuetify and flexbox rendering here, map is not rendered completely until a resize event).

I tried the https://github.com/KoRiGaN/Vue2Leaflet/issues/96#issuecomment-341459943 solution, but it does not work in my case as my component is mounted when the map object is created (but not necessarily rendered yet). Then, the timeout is not working and it seems super hacky to make it work.

I tried with the load event on the map, without any luck so far (the event does not seem to be triggered actually… :/).

Did anyone manage to find a working solution for this issue? Thanks!

@Phyks if you check the code coming from this pr: #200 you will see that I added an event called: load
that you can listen to with @load on the l-map component.

You will need to use the code from my fork to have it for now

@BrandonDevaleriola I'm having this double quote problem, which is causing the GET request to fail. Is there a workaround or did I somehow miss it in your response?

I've had an issue with the grey area too (also using Vuetify). But I noticed that the issue went away if I change the zoom. So, a very dirty trick that I'm doing is that I programmatically zoom in and out again when the map loads. Then all seems to be fine and it's so fast that it's barely noticeable... not a nice workaround though I admit but none of the other suggestions here helped.

For anyone having problems with vuetify this is the layout which is working for me:

<v-container class="pa-0" fluid fill-height>
        <v-layout>
            <v-flex x12>
                <l-map v-resize="onResize">
                </l-map>
            </v-flex>
        </v-layout>
</v-container>

And on resize I do:

onResize() {
    this.$refs.map.mapObject.invalidateSize();
}

Hello guys, If you are facing such problem with vuetifyjs it's most probably that the map container is initialized with a small size.

To solve this problem you need to insure that you are loading the map whenever the container is taking the actual size.

For example, if you are using dialog as a container for the map, all what you have to do is using 'lazy' prop to insure that the content is rendered on mounted; in other words, the map must be rendered only if the dialog is activated 🎉

<v-dialog v-model="dialog" lazy>
    <l-map
      :zoom="zoom"
      :center="center"
      :style="mapStyle"
      @update:center="centerUpdate"
      @update:zoom="zoomUpdate">
      <l-tile-layer
        :url="url"
        :attribution="attribution"/>
      <l-marker :lat-lng="marker">
      </l-marker>
    </l-map>
</v-dialog>

I hope this will help some one 😄

Building for production, what worked for me was:

import 'leaflet/dist/leaflet.css';
import * as L from 'leaflet';
import * as vl from 'vue2-leaflet';
delete (vl as any).L.Icon.Default.prototype._getIconUrl;
(vl as any).L.Icon.Default.mergeOptions({
  iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
  iconUrl: require('leaflet/dist/images/marker-icon.png'),
  shadowUrl: require('leaflet/dist/images/marker-shadow.png'),
});

I'm using TypeScript. And strangely enough, my editor complains that "vue2-leaflet has no exported member L". But still, doing it that way the workaround works.

It's also necessary to have import * as L from 'leaflet';.


Edit: The workaround I was using was working only for local development with Vue CLI. But didn't work while building for prod.

It's working properly for me, after a couple configurations: --- For the icons not showing up when using Webpack, there's now a plug-in to solve it at the Leaflet level: https://github.com/ghybs/leaflet-defaulticon-compatibility My `main.ts` looks now like:
import '@babel/polyfill';
// Import Component hooks before component definitions
import './component-hooks';
import Vue from 'vue';
import './plugins/vuetify';
import './plugins/vee-validate';
import App from './App.vue';
import router from './router';
import store from '@/store';
import './registerServiceWorker';
import 'vuetify/dist/vuetify.min.css';
import 'leaflet/dist/leaflet.css';
// Re-uses images from ~leaflet package
import 'leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.webpack.css';
import * as L from 'leaflet';
import 'leaflet-defaulticon-compatibility';

Vue.config.productionTip = false;

new Vue({
  router,
  store,
  render: (h) => h(App),
}).$mount('#app');
--- I'm using Vuetify. And I was having a gray area under the map, but I wasn't setting the `zoom` (my error). After setting a zoom higher than the default, the whole map region has rendered tiles properly. The relevant section in my `.vue` file now looks like:
<v-responsive :aspect-ratio="16/9">
  <LMap @click="onMapClick"
    :zoom="5"
  >
    <LTileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"></LTileLayer>
    <LMarker v-if="point" :lat-lng="point"></LMarker>
  </LMap>
</v-responsive>
And then in the script, as part of the component:
  public onMapClick(e) {
    this.latitude = e.latlng.lat;
    this.longitude = e.latlng.lng;
  }

  get point() {
    if (this.latitude !== null && this.longitude !== null) {
      return {lat: this.latitude, lng: this.longitude};
    }
  }
I'm using TypeScript, but the plug-ins and configurations should work the same for normal JavaScript.

Hi, I was done all stuff that guys said on top, but when I want to use lmap component in v-card of vuetify it disappears. What should i do to fix this? any suggestion?

Sulotion

I resolve this problem by writing a div tag around lmap and give 100% width and 500px (customizable) height for style

Btw, I am using iView UI Library

I was experiencing the same issue of leaflet tile not fully loading on the map.
After going through various solutions and hacks suggested above., I finally managed to solve the issue by calling: this.$refs.map.mapObject.invalideSize() inside mounted lifecycle hook.
Plus I set a hard-coded height and width of the map's parent container <div>.
And lastly, added the following code inside main.js file

(this step is though added in docs of vue2-leaflet when going through the installing section)

```
import { L } from 'vue2-leaflet';
delete L.Icon.Default.prototype._getIconUrl;

L.Icon.Default.mergeOptions({
iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
iconUrl: require('leaflet/dist/images/marker-icon.png'),
shadowUrl: require('leaflet/dist/images/marker-shadow.png')
});```

I have the same problem (Nuxt + Vuetify).
The solution of Alhakem works a little (add the "lazy" paramater) in the Ex

<v-bottom-sheet v-model="sheetMap" full-width lazy>

This works at the first display of the v-bottom-sheet. But, after resizing the browser window and clicking on the button to show the v-bottom-sheet : only a part of the map is displayed..

In another page of the web site, I have a map in a tab panel (https://vuetifyjs.com/en/components/tabs) and I have the same issue : only a part ok the map is displayed.

IF you have an universal solution...

Hi @kaboume ,

This works at the first display of the v-bottom-sheet. But, after resizing the browser window and clicking on the button to show the v-bottom-sheet : only a part of the map is displayed.

As I mentioned above this issue happens because the changes in the container dimensions. However, I recommend you to overcome this issue by force Vue to re-render JUST the map component whenever the button is clicked to show the v-bottom-sheet

Keep in mind that if you find yourself needing to force re-rendering any component, maybe you aren't doing something the right way.

In another page of the web site, I have a map in a tab panel (https://vuetifyjs.com/en/components/tabs) and I have the same issue : only a part ok the map is displayed.

You can solve this the same way by using 'lazy' prop in v-tab-item tag

Hi @Alhakem ,
Thank you for the v-tab-item tag! I did not see that we could also use the "lazy" parameter in the v-tab!

Fot the sheet, the solution to rerender the Map works great!!! (I used the solution described at the bottom of this page : http://michaelnthiessen.com/force-re-render/).

I setted a key parameter on the map

<l-map :key="componentKey" ref="refMap" :zoom="10" :center="positionInitiale">

and change the value of the componentKey after cliquing on the button to display the sheet

forceRerender() {
      this.componentKey = this.componentKey + 1;
    },
watch: {
    sheetMap: function(val) {
      if (val == true) {
        this.$nextTick(() => {
          this.$refs.refMap2.forceRerender();
        });
      }
    }
  },

Big thanks to all!

@kaboume well done 😊👍

Is anyone in this thread up to write down in the docs some guidelines that are scattered here? It would be helpful for the next people with the same problems

@DonNicoJs

I can help with that

@Alhakem Thanks! Waiting for a docs PR so :)

Screenshot from 2019-06-01 13-24-18

this is absolutely crazy!~...............................when it happens, it can cause my others vue components being blank like this..........

peekUXXZ2Z

When I resize it restores, can someone help me?

@GopherJ are you using vuetify ? have you read and tried the solution posted in this issue ?

@DonNicoJs No, I'm not using vuetify. Why when I change tab it influences the map? By the way I'm using keep-alive for router

Hi @Alhakem ,
Thank you for the v-tab-item tag! I did not see that we could also use the "lazy" parameter in the v-tab!

Fot the sheet, the solution to rerender the Map works great!!! (I used the solution described at the bottom of this page : http://michaelnthiessen.com/force-re-render/).

I setted a key parameter on the map

<l-map :key="componentKey" ref="refMap" :zoom="10" :center="positionInitiale">

and change the value of the componentKey after cliquing on the button to display the sheet

forceRerender() {
      this.componentKey = this.componentKey + 1;
    },
watch: {
    sheetMap: function(val) {
      if (val == true) {
        this.$nextTick(() => {
          this.$refs.refMap2.forceRerender();
        });
      }
    }
  },

Big thanks to all!

Hi,
There is a disadvantage to using the lazy property in the v-tab-item tag : SEO ! With a nuxt project in universal mode, the content of the v-tab-item is not created by the server side : when displaying the page soutce, the tab item is empty :( So, it's not the best solution when we have to optimize the SEO of the page.

Hi,
In order to work with SEO, I added a watch function when tab item is selected :

watch: {
    tabSelected: function(tabItem) {
      // Refresh the map of the tab
      this.$nextTick(() => {
        if (this.$refs.refCarteEtablissements) {
          this.$refs.refCarteEtablissements.map(ref => {
            ref.forceRerender();
          });
        }
      });
    }
  },

And I my CarteEtablissement component

<l-map
        :zoom="13"
        :center="positionInitiale"
        :key="componentKey"
        style="z-index:1"
      >
....
</l-map>
 data() {
    return {
       ....
      componentKey: 0,
      ...
    };
  },
 methods: {
    forceRerender() {
      this.componentKey = this.componentKey + 1;
    },
}

And now the map displays correctly and when I show the source of the page, I see the text for the SEO.

I am so sorry, is there any way to use local set of tiles? from assets folder for example. I can't figure out how to define {z}/{x}/{y} path to local tile set folder.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Posting this here since it seems related. When viewing the examples for custom icons one vue2-leaflet's docs the re-sizable baseball icon that uses an l-icon tag strangley does not work in the provided code on the docs and jsfiddle (https://codesandbox.io/s/51sx6) although it does on the example in the docs itself (https://vue2-leaflet.netlify.com/examples/custom-icons.html). I found a fix for this by simply binding icon-url to a data property that uses require("yourImageDirectoryGoesHere").

Another thing to note is that I am also using the solution posted by @azurelogic in the main.js.

@dstearle Could you open a new issue to fix the docs? It would be greatly appreciated

Quick update for those using vuetify 2.0. The lazy prop does not longer work, but v-lazy works:

  l-map

That will do the trick!

I have a situation where my <l-map> component is inside a Vuetify stepper, and the problem is that it renders all of the <v-stepper-content> components at once, but then not the map. I'm not that experienced with Vue so maybe this is totally wrong, but it seems to be that the map is pre-initizalied at too-small a size and then when you step to the one with the map, it doesn't load properly and you get the grey square with the map tiles bunched up in the upper-left-hand corner.

I tried many solutions here but the only one I could get to work was to set a watcher on the variable controlling the stepper and use @azurelogic's setTimeout-based solution if the stepper variable is the one with the map on it, like this:

Template

```





Continue





:url="mapInitData.basemap.url"
:attribution="mapInitData.basemap.attribution"
/>




## Script

import ClientOnly from "vue-client-only";
import { LMap, LTileLayer, LGeoJson } from "vue2-leaflet";
import { Icon } from "leaflet";
delete Icon.Default.prototype._getIconUrl;
Icon.Default.mergeOptions({
iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"),
iconUrl: require("@/assets/marker-icon.png"),
shadowUrl: require("leaflet/dist/images/marker-shadow.png")
});

export default {
components: {
LMap,
LTileLayer,
ClientOnly
},
data() {
return {
stepper: 1,
mapInitData: {
basemap: {
attribution:
OpenStreetMap contributors',
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
}
},

watch: {
stepper: function (val) {
if (val === 2) {
this.reloadMap();
}
}
},
methods: {
reloadMap: function() {
console.log("reloading map");
// this.$refs.map.mapObject.invalidateSize(); // Doesn't work!
setTimeout(function() { window.dispatchEvent(new Event('resize')) }, 250);
}
}
};
```

Quick update for those using vuetify 2.0. The lazy prop does not longer work, but v-lazy works:

  l-map

That will do the trick!

This guy right here! pin this somewhere!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

shtw picture shtw  ·  5Comments

mpallante picture mpallante  ·  4Comments

lufias picture lufias  ·  3Comments

Abdelaziz18003 picture Abdelaziz18003  ·  4Comments

carlmjohnson picture carlmjohnson  ·  4Comments