Leaflet Marker not found production env

25,891

Solution 1

This is a known bug in Leaflet, the root issue is that Leaflet's icon image location is been wrongly referenced during bundling.

You can verify that this is your issue, buy validating this parameter (in run time): L.Icon.Default.prototype._getIconUrl().
The correct value should be <some_directory>/leaflet/dist/images/.
However if this bug is happening to you, it's value is: data:image/png;base64,iVBO....K5CYII=")undefined

There are different solutions (work around) depending on which bundle-loader you are using (Vanila WebPack, Angular-Cli - superset of WebPack, etc...).

You can see the original issue here (as well as different solutions depending on your bandle-loader):
https://github.com/Leaflet/Leaflet/issues/4968

If you are using Angular-Cli this solution will work for you. Add this code somewhere before setting the Maker:

import { icon, Marker } from 'leaflet';
const iconRetinaUrl = 'assets/marker-icon-2x.png';
const iconUrl = 'assets/marker-icon.png';
const shadowUrl = 'assets/marker-shadow.png';
const iconDefault = icon({
  iconRetinaUrl,
  iconUrl,
  shadowUrl,
  iconSize: [25, 41],
  iconAnchor: [12, 41],
  popupAnchor: [1, -34],
  tooltipAnchor: [16, -28],
  shadowSize: [41, 41]
});
Marker.prototype.options.icon = iconDefault;

(this code will change the broken Marker's url, to a valid image from your assets folder).

And add this code at you angular.json (for Angular version >= 6.x) or at your angular-cli.json (for Angular version <= 5.x):

"assets":
[
   "src/favicon.ico",
   "src/assets",
   {
      "glob": "**/*",
      "input": "node_modules/leaflet/dist/images/", // you may need to change this path, according to your files structure
      "output": "./assets/"
   }
] ...

(this code will copy the original Marker images to the /assets folder so angular-cli could load them)

Solution 2

import "leaflet/dist/images/marker-shadow.png";

if using webpack, just import the image

Solution 3

Below code worked for me

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")
});

Solution 4

Working in Angular 10

For me worked copy paste PNGs files to assets and one command

ngOnInit() { L.Icon.Default.ImagePath = "assets/leaflet/" }

For Angular 13 with Leaflet 1.7

ngOnInit() { L.Icon.Default.imagePath = "assets/leaflet/" }

Solution 5

I ran into a similar issue using Parcel as the bundler in combination with TypeScript and Leaflet v1.4 (installed via npm, as well as its typings) and solved it using Gil's answer in a slightly different way.

import 'leaflet/dist/leaflet.css';
import 'leaflet-draw/dist/leaflet.draw.css';
import L, {
  LatLngExpression,
  FeatureGroup,
  TileLayerOptions,
  LayerEvent,
  LeafletMouseEvent,
  Marker,
  Layer,
  icon,
  LayerGroup,
  GeoJSON
} from 'leaflet';
import 'leaflet-draw';
import iconRetinaUrl from './assets/marker-icon-2x.png';
import iconUrl from './assets/marker-icon.png';
import shadowUrl from './assets/marker-shadow.png';

// Assign the imported image assets before you do anything with Leaflet.
Marker.prototype.options.icon = icon({
  iconRetinaUrl,
  iconUrl,
  shadowUrl,
  iconSize: [25, 41],
  iconAnchor: [12, 41],
  popupAnchor: [1, -34],
  tooltipAnchor: [16, -28],
  shadowSize: [41, 41],
});

And in another file, I added the required declarations so TypeScript allows me to import png images, e.g.

declare module "*.png" {
  const content: string;
  export default content;
}

Also notice that, in case you use a Leaflet plugin that requires access to these images, you may need to explicitly assign it too, e.g. the Leaflet draw plugin required it as well. Example:

        map.addLayer(drawLayer);
        const drawControl = new L.Control.Draw({
          draw: {
            circle: false,
            circlemarker: false,
            marker: {
              icon: Marker.prototype.options.icon, // Assign icon explicitly
            },
          },
          edit: {
            featureGroup: drawLayer,
          },
        });
        map.addControl(drawControl);
        map.on(L.Draw.Event.CREATED, event => {
          const layer = (event as LayerEvent).layer;
          drawLayer.addLayer(layer);
        });
Share:
25,891

Related videos on Youtube

maluss
Author by

maluss

Updated on April 26, 2022

Comments

  • maluss
    maluss about 2 years

    I got a problem with leaflet.

    Everything is working fine in development, but in production, my app isn't able to locate the marker-icon.png and marker-shadow.png images.

    It is looking for the path assets/station/images/marker-icon.png

    Leaflet js is including like this in my html.erb file

    <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.5/leaflet.js"></script>
    
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.5/leaflet.css" />
    

    If someone can help!

    • IvanSanchez
      IvanSanchez over 7 years
      Which version of Leaflet? How do you deploy the Leaflet JS and CSS files? Where do those files come from (framework, manual download, etc)?
    • maluss
      maluss over 7 years
      I've edit my post!
    • Alex G Rice
      Alex G Rice over 7 years
      Please review my answer below, and upvote or mark as as accepted if it helped you.
  • ghybs
    ghybs almost 6 years
    While this could be a nice explanation for Angular, notice that OP never mentioned using a JS bundler, their incorrect path is not a data URI, and includes old (0.x) Leaflet assets from CDN.
  • Admin
    Admin over 4 years
    Are you sure this is offering anything new that the other answers are not already offering?
  • user43284
    user43284 over 4 years
    Is it a nice one-line fix.
  • Juanma Font
    Juanma Font about 4 years
    This solution is simple, and will fix with all angular versions, fantastic
  • Mahesh Vemula
    Mahesh Vemula about 4 years
    got error with above code snippet. Error: "TS2591: Cannot find name 'require'. Do you need to install type definitions for node? Try npm i @types/node and then add node to the types field in your tsconfig"
  • Kelvin Cayman
    Kelvin Cayman almost 4 years
    Awesome. This fixed my issue. I am using ngx-leaflet. Thanks!
  • Philipp Doerner
    Philipp Doerner over 3 years
    I concur with user43284 - Lesser code that is simpler is in my opinion better and cleaner. I can confirm this worked for Angular10
  • evendiagram
    evendiagram about 3 years
    This fix works for a vite (rollup) & vue3 build. Go into node_modules/leaflet/dist/images and pluck those three marker png files. Stick them in public/assets and build away.
  • edjm
    edjm almost 3 years
    I'm not clear, where are you saying to add this bit of code to?
  • Thorvald
    Thorvald almost 3 years
    @NashGC can you elaborate on how you did this with Nuxt? I'm importing leaflet locally into one single component instead of using a plugin, where did you place this block of code?
  • NashGC
    NashGC almost 3 years
    @Thorvald, I've used it in mounted() method of a map component. (component where I use map)
  • Thorvald
    Thorvald over 2 years
    @NashGC This does work for me.. however my marker disappears in Safari browsers. Any idea how this would be fixed? I'm on leaflet 1.7.1, vue2-leaflet 2.7.0
  • Denis Molodtsov
    Denis Molodtsov over 2 years
    getting error Property '_getIconUrl' does not exist on type 'Default' Using create-react-app with TypeScript
  • dlg_
    dlg_ over 2 years
    Thanks, it worked for me too ... but without the require function. I just added iconUrl: "/webroot/assets/js/leaflet/images/marker-icon.png", and so on ... of course fitting to my base url.
  • Dinesh Shekhawat
    Dinesh Shekhawat almost 2 years
    Should have been the accepted answer. This works like a charm. Thanks!