[GIS] Using custom marker name in Leaflet from external GeoJSON file

geojsonleafletmarkers

I have the following code (which works great):

<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js" integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og==" crossorigin=""></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.js" type="text/javascript"></script>
</head>
<body>

<div id="map" style="width: 60%; height: 400px;"></div>

<script>

    // Center the map
    var map = L.map('map').setView([54.233669, -4.406027], 6);


    // Attribution
    L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
        maxZoom: 18,
        attribution: 'Map &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a>',
        id: 'mapbox.streets'
    }).addTo(map);


    // Icon properties
    var LeafIcon = L.Icon.extend({
        options: {
           iconSize: [32, 37],
           iconAnchor: [10, 32],
           popupAnchor: [5, -30]
        }
    });


    // Prepare some icons
    var Icon1 = new LeafIcon({ iconUrl: 'images/pins/1.png' }),
        Icon2 = new LeafIcon({ iconUrl: 'images/pins/2.png' }),
        Icon3 = new LeafIcon({ iconUrl: 'images/pins/3.png' }),
        Icon4 = new LeafIcon({ iconUrl: 'images/pins/4.png' }),
        Icon5 = new LeafIcon({ iconUrl: 'images/pins/5.png' });



    function forEachFeature(feature, layer) {
        var popupContent = "<p> <b>" + feature.properties.Misc +
            "</b><br />" + feature.properties.Street +
            "<br />" + feature.properties.City +
            ",<br /> " + feature.properties.State +
            ", " + feature.properties.Zip + "</p>";
        layer.bindPopup(popupContent);
    }



    $.getJSON("points.json", function (data) {
        locations = L.geoJson(data, {
            onEachFeature: forEachFeature,
            pointToLayer: function (feature, latlng) {
                return L.marker(latlng, { icon: Icon4 })
            }
        });

        map.fitBounds(locations.getBounds(), { padding: [25, 25] });
        locations.addTo(map);

    });

</script>

</body>
</html>

However, I wish each marker on the map to use a custom marker name that's specified in the GeoJSON file.

My points.json file contains:

{
"type": "FeatureCollection",
"features": [

    {
        "type": "Feature",
        "geometry": { "type": "Point", "coordinates": [-3.216660, 54.456070] },
        "properties": {
            "Icon": "Icon1",
            "Street": "street one",
            "City": " city here",
            "State": " state here",
            "Zip": " ZIP 1",
            "Misc": " some misc 1"
        }

    },

    {
        "type": "Feature",
        "geometry": { "type": "Point", "coordinates": [-0.551140, 51.202747] },
        "properties": {
            "Icon": "Icon2",
            "Street": "street TWO",
            "City": " city here",
            "State": " state here",
            "Zip": " ZIP 2",
            "Misc": " some misc 2"
        }

    }

]
}

Note the Icon property.

In my HTML code, if I change this:

                    return L.marker(latlng, { icon: Icon4 })

To:

                    return L.marker(latlng, { icon: feature.properties.Icon })

I get the following error in the browser console and no marker is displayed:

TypeError: t.icon.createIcon is not a function

What am I doing wrong here and how do I correct this?

Best Answer

What's wrong is that you are defining markers on the basis of feature property Icon, which is string, instead of using corresponding icon object.

On possible solution is to define all icons as object, where properties are names of icons and values are icon objects. Then it's easy to reference icon by it's name.

Relevant code would then look something like this:

var myIcons = {
    Icon1: new LeafIcon({ iconUrl: 'images/pins/1.png' }),
    Icon2: new LeafIcon({ iconUrl: 'images/pins/2.png' }),
    Icon3: new LeafIcon({ iconUrl: 'images/pins/3.png' }),
    Icon4: new LeafIcon({ iconUrl: 'images/pins/4.png' }),
    Icon5: new LeafIcon({ iconUrl: 'images/pins/5.png' })
};

$.getJSON("points.json", function (data) {
    locations = L.geoJson(data, {
        onEachFeature: forEachFeature,
        pointToLayer: function (feature, latlng) {
            return L.marker(latlng, {icon: myIcons[feature.properties.Icon]})
        }
    });
    map.fitBounds(locations.getBounds(), {padding: [25, 25]});
    locations.addTo(map);
});