Thanks to TomazicM I got my GeoJSON import up and running. Here's the whole code, I highlight the problematic bits thereafter

    var map = L.map('map', {
        crs: L.CRS.Simple,
        minZoom: -1

//TODO - set the image width and height to w and h
    var w = 1700;
    var h = 2200;

    //the stuff to be able to work with xy instead of latlon
    var yx = L.latLng;
    var xy = function(x, y) {
        if (L.Util.isArray(x)) {    // When doing xy([x, y]);
            return yx(x[1], x[0]);
        return yx(y, x);  // When doing xy(x, y);

    var bounds = [xy(0, 0), xy(w, h)];
        map.fitBounds(bounds).setMaxBounds(bounds); //this makes the map fit the bounds we just defined

//TODO - insert image name here
    var image = L.imageOverlay('img/kisandra.png', bounds).addTo(map);

    //create map icon class by extending L.Icon
    var TaladasIcon = L.Icon.extend({
    options: {
        iconSize:     [50, 50],
        iconAnchor:   [25, 25],
        popupAnchor:  [0, -25]

    //define custom icons
    var metropolisIcon = new TaladasIcon({iconUrl: '/img/metropolis.png'}),
        cityIcon = new TaladasIcon({iconUrl: 'img/city.png'}),
        townIcon = new TaladasIcon({iconUrl: 'img/town.png'}),
        villageIcon = new TaladasIcon({iconUrl: 'img/village.png'}),
        innIcon = new TaladasIcon({iconUrl: 'img/tankard.png'});

    var featureIcon     = villageIcon;

function forEachFeature(feature, layer) {
        var popupContent = "<p> <b>" + feature.properties.name + 
            "</b></br>"+ feature.properties.description +
            "</br>"+ feature.properties.icon +"</p>";
        featureIcon     = window[feature.properties.icon];

var jsonMetropolis = L.geoJSON([], {
  onEachFeature: forEachFeature,
  pointToLayer: function (feature, yx) {
    return L.marker(yx, {
      icon: featureIcon

  dataType: "json",
  url: "data/metropolises.geojson",
  success: function(data) {
    $(data.features).each(function(key, data) {

As you can see after I defined my custom icons, I define a variable called featureIcon and assign it a random icon, just so it's not null var featureIcon = villageIcon; Then in the forEachFeature function I set the variable equal to a field in the geoJSON featureIcon = window[feature.properties.icon]; and later the marker is told to use said icon L.marker(yx, {icon: featureIcon})

Funny thing is, while the popups that are set in forEachFeature too, are displaying correctly the property field's contents, the icon is offset by one feature. The first feature displays the pre-set icon, the second feature displays the icon specified in feature 1, the third the icon from feature 2, etc.

What am I doing wrong this time? Wrong use of the global variable thing window[] ? Or `something else?

  • 2
    Put all marker-initialization-related code in pointToLayer. You're messing up the scope of the variables horribly in there, hoping that there won't be race conditions at all (i.e. you're hoping that each pass of onEachFeature happens just before each pass of pointToLayer -which is wrong because onEachFeature receives the already-instantiated marker so it happens after- and there's no guarantee of that synchronicity at all). – IvanSanchez Apr 26 at 14:39
  • Thank you! I didn't realize that, working now :-) – Merion Apr 26 at 19:45

The comment from IvanSanchez resolved this.

Moving featureIcon = window[feature.properties.icon]; from the forEachLayer function to the pointToLayer function did the trick.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.