JS Weird Question: Object property undefined ... but it's there

JS Weird Question: Object property undefined ... but it's there
0.0 0

#1

any JS experts there that can explain this?

Both getOrigin() and getDestination() from an API returns an object.

If I immediately query the object (orgPoint.geometry.coordinates), I get undefined value.

But if I give it a time delay of 0ms, I get the correct values?

        // returns an Object
        orgPoint = directions.getOrigin();
        destPoint = directions.getDestination()

        // why is this not working?
        console.log('...... no time delay .......');
        console.log('Origin: ', orgPoint.geometry.coordinates);         // returns undefined in Console
        console.log('Destination: ', destPoint.geometry.coordinates);   // returns undefined in Console

        // but give it a time delay of 0ms, it works
        console.log('...... now using setTimeout() .......');
        setTimeout(function(){
            console.log('Origin(t): ', orgPoint.geometry.coordinates);     // returns correct value
            console.log('Destination(t): ', destPoint.geometry.coordinates);  // returns correct value
        }, 0);

Result:

...... no time delay .......
Origin:  undefined
Destination:  undefined

...... now using setTimeout() .......
Origin(t):  [-87.0353, 35.6151]
Destination(t): [-86.7744, 36.1622]

#2

Maybe those two functions are asynchronous? Though that seems odd.


#3

It may be async. That was my first thought… but if I go to the browser console, and type the variables orgPoint and destPoint, the whole object tree is right there. I can see the values.

If I type in the browser, orgPoint.geometry.coordinates, I get the correct answer.

So one would assume, okay it works. Now I can use it in my JS code.
But assigning via JS code myvalue = orgPoint.geometry.coordinates (the same code I used in console)
produces undefined.

The only way for JS code to work is wrapping it in a timeout() function.


#4

Maybe it is returning a Promise?


#5

Here’s the relevant Github source

the getOrigin() and getDesination() methods don’t have callbacks.

In the source code, it’s a simple return statement.

getOrigin: function () {
        return this.origin;
},

getDestination: function () {
        return this.destination;
},


#6

Can you post a link to your current code? I have been reading through all the documentation for mapbox and wanted to test some ideas I have about what could be the problem. I could not find anything online about problems with getOrigin or getDestination.


#7

I dont have this demo code on github, but here’s the complete code. It’s only short.

UPDATE: Okay, after playing with this some more, I do think I need a time delay (1 second is good enough) to allow the getOrigin and getDestination sufficient time to do their geocoding. Otherwise, the getDestination() part may have undefined values.

This demo code works, I’m okay with this now. Now, I can properly integrate this with my project.

Thanks for looking into this, and here’s the working code if you want to play with it.
Just replace with your own access token/api-key.

PS: It’s sprinkled with lots of comments as I was trying to figure this out and learning it.

<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Driving directions</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.mapbox.com/mapbox.js/v3.1.1/mapbox.js'></script>
<link href='https://api.mapbox.com/mapbox.js/v3.1.1/mapbox.css' rel='stylesheet' />
<style>
  body { margin:0; padding:0; }
  #map { position:absolute; top:0; height:600px; width:100%; }
</style>
</head>
<body>
<style>
#inputs,
#errors,

#inputs {
    z-index: 10;
    top: 410px;
    left: 10px;
    position: relative;
    width: 500px;
    margin: auto;
}

#directions {
    background: rgba(0,0,0,.8);
    top: 620px;
    overflow: auto;
    width: 90%;
    margin: auto;
    position: relative;
    height: 400px;
}

#errors {
    z-index: 8;
    opacity: 0;
    padding: 10px;
    border-radius: 0 0 3px 3px;
    background: rgba(0,0,0,.25);
    top: 90px;
    left: 10px;
}

</style>

<script src='https://api.mapbox.com/mapbox.js/plugins/mapbox-directions.js/v0.4.0/mapbox.directions.js'></script>
<link rel='stylesheet' href='https://api.mapbox.com/mapbox.js/plugins/mapbox-directions.js/v0.4.0/mapbox.directions.css' type='text/css' />

<div id='map'></div>
<div id='inputs'></div>
<div id='errors'></div>
<div id='directions'>
  <div id='routes'></div>
  <div id='instructions'></div>
</div>

<script>
    var startAddress = 'Denver, CO';
    var endAddress = 'St. Louis, WA';
    
    var orgPoint, destPoint;
    L.mapbox.accessToken = 'replace-with-own-api-key';

    var map = L.mapbox.map('map', 'mapbox.streets', {
        zoomControl: true
    }).setView([39, -98.5], 5); // center of USA

    // move the attribution control out of the way
    map.attributionControl.setPosition('bottomright');

    // create the initial directions object, from which the layer
    // and inputs will pull data.
    var directions = L.mapbox.directions({
        profile: 'mapbox.driving',
        units: 'imperial'
    });

    var directionsLayer = L.mapbox.directions.layer(directions)
        .addTo(map);

    //* input form fields for start and ending locations
    //* but this is not required to be shown if we use setOrigin() and setDestination() methods 
    // var directionsInputControl = L.mapbox.directions.inputControl('inputs', directions).addTo(map);

    //* hide error messages
    // var directionsErrorsControl = L.mapbox.directions.errorsControl('errors', directions).addTo(map);

    //* the actual route to get from origin to destination
    var directionsRoutesControl = L.mapbox.directions.routesControl('routes', directions).addTo(map);

    //* textual instructions
    var directionsInstructionsControl = L.mapbox.directions.instructionsControl('instructions', directions).addTo(map);

    map.on('ready', function(){
        // set the origin and destination point.
        directions.setOrigin(startAddress);     // Start point, array[lng, lat] or address, or place name
        directions.setDestination(endAddress);  // End point, array[lng, lat] or address, or place name    

        // check if we have valid origin and destination points
        if (directions.queryable()) {
            // if valid origin and destination points, fire the query
            // this will drop the origin and destination map markers and draw the routes
            directions.query();

            // Q: How do we zoom so origin and destination points are shown automatically?
            // A: we use .fitbounds() 

            // Q: but how do we get lat/lng of our origin and destinations.
            // A: do a getOrigin() and getDestination(), and extract lat/lng object

            // getXXXX() returns an Object
            orgPoint = directions.getOrigin();
            destPoint = directions.getDestination();
            
            setTimeout(function(){
                // so Directions API automatically makes (2) additional Geocoding API call$
                // converting origin and destination from simple string to a lat/lng coordinates
                // map.fitBounds(bounds);
                orgPoint = orgPoint.geometry.coordinates;
                destPoint = destPoint.geometry.coordinates;

                map.fitBounds([
                    [orgPoint[1], orgPoint[0]],     // NOTE COORDINATES!!! It's ass backwards for this method()! 
                    [destPoint[1], destPoint[0]]
                ]);
            }, 1000);  // give it sufficient time delay, otherwise, getting destination lat/lng may fail. 
        }
        
    })

</script>
</body>
</html>