Andy-H Posted March 21, 2012 Share Posted March 21, 2012 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. Quote Link to comment Share on other sites More sharing options...
Andy-H Posted March 21, 2012 Author Share Posted March 21, 2012 Sorry, didn't realise I was still in misc, does this need moving? Quote Link to comment Share on other sites More sharing options...
Andy-H Posted March 21, 2012 Author Share Posted March 21, 2012 Ran it through the geocoding api first. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.