0

I'm trying to display three maps on one page with leaflet.js and mapquests API. I've created a jQuery for-loop to read a unique postal code, then convert it (via mapquest) to latitude and longitude, and lastly create three maps, and add the matching latitude and longitude to every map. This would then result in three different maps, displayed on one page.

I've somewhat achieved this with the code below. Every "card" has its own card number (from 1 to 3), postal code, and corresponding map and map ID.

Edit I also get this jQuery error:

jQuery.Deferred exception: t is undefined addTo@https://unpkg.com/leaflet@1.6.0/dist/leaflet.js:5:64070
MYWEBSITEURL/map.js:38:6
e@https://code.jquery.com/jquery-3.5.1.min.js:2:30005
l/</t<@https://code.jquery.com/jquery-3.5.1.min.js:2:30307
 undefined

The issue I now have is that when my jQuery runs I get this console error: TypeError: t is undefined. I've narrowed it down to it having something to do with either the L.tileLayer() and/or L.circle() functions. I've added the latest jQuery and leaflet.js to the page.

Edit Changed the question after @ivansanchez answer, but still the same issues.

Edit Changed the leaflet.js to leaflet-sr.js and I now get the error: TypeError: map is undefined

map.js:

$(document).ready(function(){

var postalCodes = [];
var latLng      = [];
var lat         = [];
var lng         = [];
var maps        = [];

for(var n = 0; n < 3; n++) {

    console.log('Maps for-loop count: '+n);

    postalCodes.push($('.card'+[n]+' .card__info--postal > span').html());

    $.ajax({ // Get lat & long from postal code
        async: false,
        url: 'https://www.mapquestapi.com/geocoding/v1/address?key=MYAPIKEY&postalCode='+postalCodes[n]+'&country=finland',
        success: function(obj) {
            latLng[n] = obj.results[0].locations[0].latLng;
            lat[n]    = latLng[n].lat;
            lng[n]    = latLng[n].lng;
        },
        error: function(xhr) {
            console.log('Map error', xhr);
        }
    });

    console.log(latLng[n], lat[n], lng[n]);

    var map = L.map('mapid'+[n]).setView([lat[n], lng[n]], 13);

    L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
        maxZoom: 18,
        id: 'mapbox/streets-v11',
        tileSize: 512,
        zoomOffset: -1,
        accessToken: 'MYAPIKEY'
    }).addTo(maps[n]);

    L.circle([lat[n], lng[n]], { // Map out area of the postal code
        color: 'rgb(218, 65, 103)',
        fillColor: 'rgb(218, 65, 103)',
        fillOpacity: 0.5,
        radius: 500
    }).addTo(maps[n]);

    maps.push(map);
}
});
Krullmizter
  • 529
  • 8
  • 28

2 Answers2

1

You're doing...

 var maps = [ L.map('mapid'+[n]).setView([lat[n], lng[n]], 13) ]; // Creates three maps, that corresponds with three mapids in my HTML

...and that doesn't do what you think it does. You're creating an array of one element, and assigning it to maps, and you're doing that three times. Therefore, at the second pass of the loop, when the code refers to maps[1], that has an undefined value.

Instead, you probably want to do something like...

var maps = [];
for(var n = 0; n < 3; n++) {
   /* stuff */
   maps.push( L.map( /*stuff*/ ) );
   L.tileLayer( /* stuff */ ).addTo(maps[n]);
}

...or even...

var maps = [];
for(var n = 0; n < 3; n++) {
   /* stuff */
   var map = L.map( /*stuff*/ );
   L.tileLayer( /* stuff */ ).addTo(map);
   maps.push(map);
}

On the other hand, you have a good ol' race condition regarding the values of latLng[n], lat[n] and lng[n]. Since those values are only set when the network operation has finished (since AJAX is async, etc etc), they are (most probably) undefined when they are used. You might want to delay adding data to each map until you actually got said data, like e.g.

var maps = [];
for(var n = 0; n < 3; n++) {
   var map = L.map( /*stuff*/ );
   fetch(url)
     .then(function(response){return response.json})
     .then(function(json)){
       L.circle(json.results[0].locations[0]).addTo(map);
     });
   L.tileLayer( /* stuff */ ).addTo(map);
   maps.push(map);
}

Note that the sequence of events here is: a network request is initiated, the map is instantiated, the tilelayer is instantiated, the network request completes, the circle gets instantiated.

(And why yes, I prefer the fetch API over jQuery any day).

Remember that, in Leaflet, it's usually nice to change leaflet.js into leaflet-src.js to get better stack traces, and that using the browser's developer tools to set breakpoints prior to exceptions will allow you to see which values are undefined at that time.

IvanSanchez
  • 18,272
  • 3
  • 30
  • 45
  • Thx for the answer, @ivansanchez I tried to change the code like your example #2, declaring the `maps` array first and then using `.push` to add each map. But I still have the same issue. I've updated the code on the question to what I now have. I still get: `jQuery.Deferred exception: t is undefined addTo@https://unpkg.com/leaflet@1.6.0/dist/leaflet.js:5:64070` and `TypeError: t is undefined`. The `latLng[n]`, `lat[n]` and `lng[n]` are not `undefined` when I try to `console.log(latLng[n], lat[n], lng[n])` them inside the `for-loop` – Krullmizter May 27 '20 at 08:34
  • Do a `console(n)` inside the ajax `sucess` callback, then wonder why that's happening, then read https://stackoverflow.com/questions/111102/how-do-javascript-closures-work – IvanSanchez May 27 '20 at 09:01
  • It loops just once, as the entire loop does. If I comment out the `L.titleLayer` and `L.circle` the loop finishes, and I get three `postalcodes`, `latLng` objects and three different maps. I changed the `leaflet.js` to `leaflet-src.js` and I now get the error of: `TypeError: map is undefined` – Krullmizter May 27 '20 at 09:23
  • Ahh now I see, stupid me I had the old `addTo(maps[n])` instead of your `addTo(map)` – Krullmizter May 27 '20 at 09:49
0

Change your code to:

maps[n] = L.map('mapid'+[n]).setView([lat[n], lng[n]], 13); 
Falke Design
  • 10,635
  • 3
  • 15
  • 30