Jump to content

Google directions API (auto correction?)


Andy-H

Recommended Posts

I'm writing a sort of fake tracking unit in nodeJS, it basically selects a random location, limits it to within some bounds, then I plan to inset the data into a database on around 30 second intervals, it's for a real-time demo of our works tracking system, however, if the latlngs don't happen to land on a road, the directions API returns an empty route, anyone know if there's a way to auto-correct the start / destination of a route?

 

 

Thanks

 

 

// directions/directions.js


var http       = require('http'), 
    latlng     = require('./latlng'),
    Polyline   = require('./polyline');


var Directions = function(origin, destination, bounds) {
   // validate origin / destination
   origin      = latlng.parse(origin);
   destination = latlng.parse(destination);
   
   // validate bounds
   if ( typeof bounds == 'string' )
      this.bounds = latlng.convertBounds(bounds);
   this.bounds.NW = latlng.parse(this.bounds.NW);
   this.bounds.SE = latlng.parse(this.bounds.SE);
   if ( !latlng.parse(this.bounds.NW) || !latlng.parse(this.bounds.SE) )
      throw 'Invalid latitude / longitude supplied in bounds';
   if ( !origin )
      throw 'Invalid latlng supplied as origin';
   if ( !destination )
      throw 'Invalid latlng supplied as destination';


   origin      = latlng.restrictToBounds(origin, this.bounds);
   destination = latlng.restrictToBounds(destination, this.bounds);
   
   /** SET DESTINATION START / END POINT */
   this.origin      = [ origin.lat,      origin.lng ];
   this.destination = [ destination.lat, destination.lng ];


   /** GET DIRECTIONS / LAT LNG's */
   this.get         = function(callBack) {
         _requestDirections(this.origin, this.destination, callBack);
   };
   /** MILES PER SECOND TO MILES PER HOUR CONVERSION */
   var MPS2MPH      = 2.23693629;
   
   /** OPTIONS FOR HTTP REQUEST */
   var opts = {
      host : 'maps.googleapis.com',
      path : '/maps/api/directions/json?origin={origin}&destination={destination}&sensor=false',
      port : 80
   };
   
   /** REQUEST DIRECTIONS API */
   var _requestDirections = function(origin, destination, callBack) {
      // replace origin and destination
      opts.path = opts.path.replace('{origin}',      origin.join(','));
      opts.path = opts.path.replace('{destination}', destination.join(','));
      // request google directions api
      http.request(opts, function(response) {
         response.data = [];
         // UTF-8 ENCODING
         response.setEncoding('utf8');
         // WHEN DATA CHUNCKS ARE RECIEVED APPEND TO RESPONSE.DATA
         response.on('data', function (data) {
            response.data.push(data);
         });
         // WHEN RESPONSE HAS COMPLETED, USE RESPONSE.DATA
         response.on('end', function(){
            var points = _parseDirections(JSON.parse(response.data.join('')).routes[0].legs[0].steps);
            if ( typeof callBack == 'function' )
               callBack.call(null, points, JSON.parse(response.data.join('')).routes[0].overview_polyline, Polyline);
         });
      }).end();
   };
   
   /** PARSE DIRECTIONS INTO LAT, LNG, SPEED, BEARING, ALTITUDE */
   var _parseDirections = function(directions) {
      var k, direction;
      var points     = [];
      for ( k in directions )
      {
         direction          = {};
         direction.latlng   = directions[k].end_location;
         direction.speed    = ((directions[k].distance.value / directions[k].duration.value) * MPS2MPH);
         direction.bearing  = latlng.getBearing(directions[k].start_location, directions[k].end_location);
         direction.altitude = 0;
         direction.delay    = directions[k].duration.value;
         points.push(direction);
      }
      return points;
   };
};
module.exports.create = function(origin, destination, bounds) {
   return new Directions(origin, destination, bounds);
};

 

 

// directions/latlng.js


/** CONVERTS NUMERIC DEGREES TO RADIANS */
toRad = function(n) {
   return n * Math.PI / 180;
}
/** CONVERTS RADIANS TO NUMERIC (SIGNED) DEGREES */
toDeg = function(n)
{
   return n * 180 / Math.PI;
}
module.exports.getBearing = function(origin, destination) {
   var lat   = [ origin.lat, destination.lat ];
   var lng   = [ origin.lng, destination.lng ];
   var R     = 6371; // km
   var dLat  = toRad(lat[1]-lat[0]);
   var dLon  = toRad(lng[1]-lng[0]);
   lat[0]    = toRad(lat[0]);
   lat[1]    = toRad(lat[1]);
   // calculate bearing
   var y = Math.sin(dLon)   * Math.cos(lat[1]);
   var x = Math.cos(lat[0]) * Math.sin(lat[1]) -
           Math.sin(lat[0]) * Math.cos(lat[1]) * Math.cos(dLon);
   var brng = toDeg(Math.atan2(y, x));
  return ( brng < 0 ) ? brng += 360 : brng;
};
module.exports.restrictToBounds = function(latlng, bounds) {
   if ( (typeof latlng.length).toLowerCase() == 'number' && latlng.length > 1 )
   {
      latlng.lat = latlng[0];
      latlng.lng = latlng[1];
   }
   if ( latlng.lat > bounds.NW.lat )
      latlng.lat = bounds.NW.lat;
   if ( latlng.lat < bounds.SE.lat )
      latlng.lat = bounds.SE.lat;
   if ( latlng.lng > bounds.NW.lng )
      latlng.lng = bounds.NW.lng;
   if ( latlng.lng < bounds.SE.lng )
      latlng.lng = bounds.SE.lng;
   return latlng;
};
module.exports.convertBounds = function(bound_name) {
   var bounds = {
      Nigeria : { 
         NW : [ 12.637242349197756, 3.927704592284158  ],
         SE : [ 7.871121752587033,  11.980683107909158 ]
      }
   };
   if ( !bounds.hasOwnProperty(bound_name) )
      return false;
   return bounds[bound_name];
};
module.exports.parse = function(latlng) {
   if ( (typeof latlng.length).toLowerCase() == 'number' && latlng.length > 1 )
   {
      latlng.lat = latlng[0];
      latlng.lng = latlng[1];
   }
   latlng.lat = parseFloat(latlng.lat);
   latlng.lng = parseFloat(latlng.lng);
   
   if ( typeof latlng.lat != 'number' || typeof latlng.lng != 'number' )
      return false;
   if ( latlng.lat < -90 || latlng.lat > 90 )
      return false;
   if ( latlng.lng < -180 || latlng.lng > 180 )
      return false;
   return latlng;
};

// directions/polyline.js


var Polyline = (function() {
   return {
      decode : function(polyline) {
         var pts   = [];
         var lat      = 0;
         var lng      = 0;
         while ( polyline.points.length )
         {
            lat += _decode(polyline);
            lng += _decode(polyline);
            pts.push({
               'lat' : (lat * 1e-5),
               'lng' : (lng * 1e-5)
            });
         }
         return pts;
      }
   };
   function _decode(polyline) {
      var shift  = 0, 
          result = 0, 
          i      = 0,
          b;
      do { // while ascii(polyline['polyline']) > ascii([space] " ")
         b       = polyline.points.charCodeAt(i++) - 63;
         result |= (b & 0x1f) << shift;
         shift  += 5;
      } while ( b >= 0x20 );
      polyline.points = polyline.points.substr(i);
      return ((result & 1) ? ~(result >> 1) : (result >> 1));
   };
})();
module.exports = Polyline;

 

 

// test.js


var start = [ 9.7, 8.4 ];
var end   = [ 9.8, 8.1 ];


var directions = require('./directions/directions').create(start, end, 'Nigeria');


directions.get(function(directions, polyline, decoder) {
   var decoded = decoder.decode(polyline);
   console.log(polyline);
});

 

 

Just in-case anyone's interested.

 

 

Thanks for any help.

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.