Google Maps API v3 - Waypoints Limit Workaround

Ruben Romão on June, 12, 2012

thanks to: lemonharpy

Example: google maps api v3 waypoints limit workaround

The code:

<script type="text/javascript" src="http://maps.google.com/maps/api/js?libraries=places,panoramio,geometry&sensor=false&language=en"></script>
<script type="text/javascript" src="js/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="js/gmap3.min.js"></script>
<script type="text/javascript" src="js/jquery-autocomplete.min.js"></script>
<link rel="stylesheet" type="text/css" href="js/jquery-autocomplete.css"/>

<script type="text/javascript">
// calculate route and display route data
$(document).ready(function() {
$('#cr').click(function () {

var markerOptions = {
draggable: false
};
var rendererOptions = {
draggable: true,
markerOptions: markerOptions
};

var directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
var directionsService = new google.maps.DirectionsService();
var lisbon = new google.maps.LatLng(38.700000, -9.183300);
var myOptions = {
zoom: 10,
center: lisbon,
mapTypeControl: true,
mapTypeControlOptions: { style: google.maps.MapTypeControlStyle.DROPDOWN_MENU },
navigationControl: true,
mapTypeId: google.maps.MapTypeId.ROADMAP,
navigationControl: true,
scrollwheel: true,
scaleControl: true,
draggable: false
};
var map = new google.maps.Map(document.getElementById("mapCanvas"), myOptions);
directionsDisplay.setMap(map);
directionsDisplay.setPanel(document.getElementById("directionsPanel"));

var stops = [];
for (var i = 0; (i < 200); i++) {
if(document.getElementById("via"+i) != null)
if(document.getElementById("via"+i).value != ''){
stops[i] = document.getElementById("via"+i).value;
index = i;
}
else
continue;
else
continue;
}

var batches = [];
var itemsPerBatch = 10; // google API max - 1 start, 1 stop, and 8 waypoints
var itemsCounter = 0;
var wayptsExist = stops.length > 0;

while (wayptsExist) {
var subBatch = [];
var subitemsCounter = 0;

for (var j = itemsCounter; j < stops.length; j++) {
subitemsCounter++;
subBatch.push({
location: document.getElementById("via"+j).value,
stopover: true
});
if (subitemsCounter == itemsPerBatch)
break;
}

itemsCounter += subitemsCounter;
batches.push(subBatch);
wayptsExist = itemsCounter < stops.length;
// If it runs again there are still points. Minus 1 before continuing to
// start up with end of previous tour leg
itemsCounter--;
}
calcRoute(batches, directionsService, directionsDisplay);
});
});

function calcRoute (batches, directionsService, directionsDisplay) {
var combinedResults;
var unsortedResults = [{}]; // to hold the counter and the results themselves as they come back, to later sort
var directionsResultsReturned = 0;

for (var k = 0; k < batches.length; k++) {
var lastIndex = batches[k].length - 1;
var start = batches[k][0].location;
var end = batches[k][lastIndex].location;

// trim first and last entry from array
var waypts = [];
waypts = batches[k];
waypts.splice(0, 1);
waypts.splice(waypts.length - 1, 1);

var request = {
origin : start,
destination : end,
waypoints : waypts,
provideRouteAlternatives: true,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
(function (kk) {
directionsService.route(request, function (result, status) {
if (status == window.google.maps.DirectionsStatus.OK) {

var unsortedResult = {
order : kk,
result : result
};
unsortedResults.push(unsortedResult);

directionsResultsReturned++;

if (directionsResultsReturned == batches.length) // we've received all the results. put to map
{
// sort the returned values into their correct order
unsortedResults.sort(function (a, b) {
return parseFloat(a.order) - parseFloat(b.order);
});
var count = 0;
for (var key in unsortedResults) {
if (unsortedResults[key].result != null) {
if (unsortedResults.hasOwnProperty(key)) {
if (count == 0) // first results. new up the combinedResults object
combinedResults = unsortedResults[key].result;
else {
// only building up legs, overview_path, and bounds in my consolidated object. This is not a complete
// directionResults object, but enough to draw a path on the map, which is all I need
combinedResults.routes[0].legs = combinedResults.routes[0].legs.concat(unsortedResults[key].result.routes[0].legs);
combinedResults.routes[0].overview_path = combinedResults.routes[0].overview_path.concat(unsortedResults[key].result.routes[0].overview_path);

combinedResults.routes[0].bounds = combinedResults.routes[0].bounds.extend(unsortedResults[key].result.routes[0].bounds.getNorthEast());
combinedResults.routes[0].bounds = combinedResults.routes[0].bounds.extend(unsortedResults[key].result.routes[0].bounds.getSouthWest());
}
count++;
}
}
}
//directionsDisplay.setDirections(combinedResults);
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(combinedResults);
$("#directionsPanel").empty();
$("#printLink").remove();
$("#directionsPanel").before("<p id='printLink'><a href='javascript:printDirections();'>Print Directions</a></p>");
} else {
alert("Sorry! Unable to determine a valid route");
}
}
}
});
})(k);
}
}

// print directions
function printDirections() {
var html = $("#directionsPanel").html();
var w = window.open();
$(w.document.body).html(html);
}

$(document).ready(function() {
// initialize map and draw it
$('#mapCanvas').gmap3({
action:'init',
options:{
zoom: 10,
maxZoom: 17,
minZoom: 7,
center: [38.700000, -9.183300],
mapTypeControl: true,
mapTypeControlOptions: { style: google.maps.MapTypeControlStyle.DROPDOWN_MENU },
navigationControl: true,
mapTypeId: google.maps.MapTypeId.ROADMAP,
navigationControl: true,
scrollwheel: true,
scaleControl: true,
draggable: false
}
},
{ action:"autofit"}
);
});

// autocomplete addreses
$(function(){
$('#fromAddress').autocomplete({
source: function() {
$("#mapCanvas").gmap3({
action:'getAddress',
address: $(this).val(),
callback:function(results){
if (!results) return;
$('#fromAddress').autocomplete(
'display',
results,
false
);
}
});
},
cb:{
cast: function(item){
return item.formatted_address;
},
select: function(item) {
$("#mapCanvas").gmap3({
action:'addMarker',
latLng:item.geometry.location,
map:{ right:true },
marker:{
options:{ draggable: false }
},
});
}
}
});
});
$(function(){
$('#via0').autocomplete({
source: function() {
$("#mapCanvas").gmap3({
action:'getAddress',
address: $(this).val(),
callback:function(results){
if (!results) return;
$('#via0').autocomplete(
'display',
results,
false
);
}
});
},
cb:{
cast: function(item){
return item.formatted_address;
},
select: function(item) {
$("#mapCanvas").gmap3({
action:'addMarker',
latLng:item.geometry.location,
map:{ right:true },
marker:{
options:{ draggable: false }
},
});
}
}
});
});
$(function(){
$('#toAddress').autocomplete({
source: function() {
$("#mapCanvas").gmap3({
action:'getAddress',
address: $(this).val(),
callback:function(results){
if (!results) return;
$('#toAddress').autocomplete(
'display',
results,
false
);
}
});
},
cb:{
cast: function(item){
return item.formatted_address;
},
select: function(item) {
$("#mapCanvas").gmap3({
action:'addMarker',
latLng:item.geometry.location,
map:{ right:true },
marker:{
options:{ draggable: false }
},
});
}
}
});
});

// add and remove stopovers
$(document).ready(function(){
i=0;
$('.del').live('click',function(){
//i--;
$(this).parent().parent().remove();
});
$('.add').live('click',function(){
i++;
$(this).val(' Del ');
$(this).attr('class','del');
var appendTxt = "<tr class='stop'><th align='right'><input type='text' size='40' id='via"+i+"' autocomplete='off' name='via"+i+"' /></th><th><input type='button' class='add' value=' Add ' /></th></tr>";
$("tr.stop:last").after(appendTxt);
$(function(){
$('#via'+i).autocomplete({
source: function() {
$("#mapCanvas").gmap3({
action:'getAddress',
address: $(this).val(),
callback:function(results){
if (!results) return;
$('#via'+i).autocomplete(
'display',
results,
false
);
}
});
},
cb:{
cast: function(item){
return item.formatted_address;
},
select: function(item) {
$("#mapCanvas").gmap3({
action:'addMarker',
latLng:item.geometry.location,
map:{ right:true },
marker:{
options:{ draggable: false }
},
});
}
}
});
});
});
});

// disables/enables save button
$(document).ready(function(){
// Set button disabled
$("input[type=button][name=create]").attr("disabled", "disabled");

// Append a change event listener to you inputs
$('input, textarea').keyup(function(){
var validation;
var start = $('#via\d').val();
var end = $('#via\d').val();

if (start == ' ' && end == ' ') {
validation = false;
}
else if (start != '' && end != '') {
validation = true;
}
// If form is validated enable form
if(validation == true) {
$("input[type=button][name=create]").removeAttr("disabled");
}
});
//Trigger change function once to check if the form is validated on page load
$('input:first').trigger('change');
});
</script>

<div id="mapCanvas" style="float:left;width:65%;height:100%;position: absolute;"></div>
<div id="control_panel" style="float:right;width:35%;text-align:left;padding-top:20px;margin: -20px;">
<table class="dynatable">
<tr>
<th align="left">Stopover(s):&nbsp;</th>
</tr>
<tr class="stop">
<th align="right">&nbsp;<input type="text" size="40" id="via0" name="via0" /></th><th><input type='button' class='add' value=' Add ' /></th>
</tr>
<tr>
<th align="right"><input type="button" name="create" id="cr" value="Claculate Route" /></th>
</tr>
</table>
<div id="directionsPanel"></div>
</div>