1

I currently have the following code which is used to display a series of GEOJson marker pins on a LeafletJS 1.5.1 map and it also displays a KML layer which can be toggled on and off using a checkbox.

    <script src="/Scripts/KML.js"></script>
    <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.REMOVED.REMOVED', {
            attribution: 'Map &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a>',
            id: 'mapbox.streets'
        }).addTo(map);


        // Popup content
        function forEachFeature(feature, layer) {

            // Author
            var Author
            if (feature.properties.By) {
                Author = " <small>(By " + feature.properties.By + ")</small>"
            } else {
                Author = ""
            }

            // Image
            var ImageContent
            if (feature.properties.ImageURL) {
                ImageContent = "<img src='" + feature.properties.ImageURL + "' width='300' height='200' /><br /><br />"
            } else if (feature.properties.YouTubeID) {
                    ImageContent = "<img src='https://img.youtube.com/vi/" + feature.properties.YouTubeID + "/hqdefault.jpg' width='300' height='200' /><br /><br />"

            } else {
                ImageContent = ""
            }

            // Comments
            var CommentsContent
            if (feature.properties.Comments) {
                CommentsContent = feature.properties.Comments + "<br /><br />"
            } else {
                CommentsContent = ""
            }

            // Build the popup content
            var popupContent = "<h4>" +
                feature.properties.Title +
                Author +
                "</h4>" +
                ImageContent +
                CommentsContent +
                "View and discuss this location";
            layer.bindPopup(popupContent);
        }


        // Build each marker
        $.getJSON("JSONMapPoints.json", function (data) {
            locations = L.geoJson(data, {
                onEachFeature: forEachFeature,
                pointToLayer: function (feature, latlng) {
                    return L.marker(latlng, {
                        icon: L.icon({
                        iconUrl: "images/pins/" + feature.properties.CategoryID + ".png",
                        iconSize: [32, 37],
                        iconAnchor: [10, 32],
                        popupAnchor: [5, -30]
                        }),
                    })
                }
            });
            map.fitBounds(locations.getBounds());
            locations.addTo(map);
        });


        // Build Locations layer
        var LayerLocations = new L.LayerGroup([
            // Not ready yet
        ]).addTo(map);


        // Build FRZ layer 
        var LayerFRZ = new L.LayerGroup([
            new L.KML("/kml/UA_FRZ_AIRAC_08_2019_CRC_E23FE9E2.kml", { async: true })
        ]).addTo(map);


        // Layer controls
        var layerControl = new L.Control.Layers(null, {
            // 'layer name': LayerFRZ,
            'Flight Restriction Zones': LayerFRZ
        }).addTo(map);


    </script>

    <div id="map" style="width: 100%; height: 600px;"></div>

I wish to also be able to toggle the GEOJson marker pins on and off too.

Which means I want to have have two check boxes, one to turn off the KML layer (already working) and one to turn off the marker pins too.

It should look like this:

Desired

I moved my geojson code in to the new L.LayerGroup code block but now I have an error in the browser console:

Error: The provided object is not a Layer.

My code now looks like this:

    <script src="/Scripts/KML.js"></script>
    <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.REMOVED.REMOVED', {
            attribution: 'Map &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a>',
            id: 'mapbox.streets'
        }).addTo(map);


        // Popup content
        function forEachFeature(feature, layer) {

            // Author
            var Author
            if (feature.properties.By) {
                Author = " <small>(By " + feature.properties.By + "</small>"
            } else {
                Author = ""
            }

            // Image
            var ImageContent
            if (feature.properties.ImageURL) {
                ImageContent = "<img src='" + feature.properties.ImageURL + "' width='300' height='200' /><br /><br />"
            } else if (feature.properties.YouTubeID) {
                    ImageContent = "<img src='https://img.youtube.com/vi/" + feature.properties.YouTubeID + "/hqdefault.jpg' width='300' height='200' /><br /><br />"

            } else {
                ImageContent = ""
            }

            // Comments
            var CommentsContent
            if (feature.properties.Comments) {
                CommentsContent = feature.properties.Comments + "<br /><br />"
            } else {
                CommentsContent = ""
            }

            // Build the popup content
            var popupContent = "<h4>" +
                feature.properties.Title +
                Author +
                "</h4>" +
                ImageContent +
                CommentsContent +
                "View and discuss this location on";
            layer.bindPopup(popupContent);
        }



        // Build Locations layer
        var LayerLocations = new L.LayerGroup([

            // Build each marker
            $.getJSON("JSONMapPoints.json", function (data) {
                locations = L.geoJson(data, {
                    onEachFeature: forEachFeature,
                    pointToLayer: function (feature, latlng) {
                        return L.marker(latlng, {
                        icon: L.icon({
                        iconUrl: "images/pins/" + feature.properties.CategoryID + ".png",
                        iconSize: [32, 37],
                        iconAnchor: [10, 32],
                        popupAnchor: [5, -30]
            }),
                        })
                    }
                });
                // map.fitBounds(locations.getBounds());
                // locations.addTo(map);
            })

        ]).addTo(map);


        // Build FRZ layer 
        var LayerFRZ = new L.LayerGroup([
            new L.KML("/kml/UA_FRZ_AIRAC_08_2019_CRC_E23FE9E2.kml", { async: true })
        ]).addTo(map);


        // Layer controls
        var layerControl = new L.Control.Layers(null, {
            'Locations': LayerLocations,
            'Flight Restriction Zones': LayerFRZ
        }).addTo(map);


    </script>

What do I need to change here to fix this?

2 Answers 2

1

So you're doing something like:

var LayerLocations = new L.LayerGroup([
    $.getJSON( /* stuff */ )
]);

You're trying to create a L.LayerGroup out of the return value of a $.getJSON call. According to the docs, that's an instance of jQuery's jqXHR, and (should be obvious) not an instance of L.Layer.

There are several approaches to this problem. I always prefer to replace all jQuery stuff with fetch just to try to get rid of the dependency on jQuery:

// Create an empty layergroup
var layerLocations = L.layerGroup();

fetch("JSONMapPoints.json")
  .then(function(response){ return response.json() })
  .then(function(data){

  // Once the browser has got the json file and has parsed it,
  // Create a L.GeoJSON out of that data...
  var locations = L.geoJson(data, { /* L.GeoJSON options, including pointToLayer */ });

  // ...and add the L.GeoJSON instance to the empty layergroup
  layerLocations.addLayer(locations);

});

An implementation with jQuery's $.getJSON instead of fetch should be trivial from this point.

Some possible approaches to the problem are, among others:

  • Creating an empty L.GeoJSON and using the addData method
  • Create the L.Control.Layers first, and use its addOverlay method after the GeoJSON data has been loaded and the L.GeoJSON instance has been created.
1
  • Your example code works perfectly - thank you so much @IvanSanchez
    – Rich
    Commented Jul 25, 2019 at 15:35
0

In the first code you had LayerLocations layer but it is not there in second, so it says it is not a layer. in the 2nd part you have locations instead of LayerLocations so the last lines should be

// Layer controls var layerControl = new L.Control.Layers(null, { 'Locations': locations, 'Flight Restriction Zones': LayerFRZ }).addTo(map);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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