I would like to drawn a \'distance traveled\' polyline over a preset route using V3 of the google maps API.
The polyline would need to run through multiple waypoints
Calculate the length of the line as you create it. Once it becomes greater than or equal to the distance "walked", stop adding points to it.
var lengthMeters = 0;
var legs = response.routes[0].legs;
for (i = 0; i < legs.length; i++) {
var steps = legs[i].steps;
for (j = 0; j < steps.length; j++) {
var nextSegment = steps[j].path;
for (k = 0; k < nextSegment.length; k++) {
if (lengthMeters <= walked) {
// if polyline is less than distance "walked", keep adding to it
polyline.getPath().push(nextSegment[k]);
if (polyline.getPath().getLength() > 1) {
var lastPt = polyline.getPath().getLength() - 1;
lengthMeters += google.maps.geometry.spherical.computeDistanceBetween(polyline.getPath().getAt(lastPt - 1), polyline.getPath().getAt(lastPt));
}
}
bounds.extend(nextSegment[k]);
}
}
}
polyline.setMap(map);
proof of concept fiddle
code snippet:
var geocoder;
var map;
var marker;
var gmarkers = [];
var METERS_TO_MILES = 0.000621371192;
var walked = (Math.round(670 * 1609.344));
//ICON
var iconImage = {
url: 'http://www.ronnieatkinson.co.uk/clients/wc2015/maps/01/mapIcons/marker_red.png', //MARKER URL
size: new google.maps.Size(20, 34), //MARKER SIZE (WxH)
origin: new google.maps.Point(0, 0), //MARKER ORIGIN
anchor: new google.maps.Point(9, 34) //MARKER ANCHOR
};
//INFO WINDOW
var infowindow = new google.maps.InfoWindow({
size: new google.maps.Size(150, 50)
});
//CREATE MARKER
function createMarker(latlng, label, html) {
var contentString = '<b>' + label + '</b><br>' + html;
var marker = new google.maps.Marker({
position: latlng,
map: map,
icon: iconImage,
title: label,
zIndex: Math.round(latlng.lat() * -100000) << 5
});
marker.myname = label;
gmarkers.push(marker);
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(contentString);
infowindow.open(map, marker);
});
return marker;
}
function initialize() {
var latlng = new google.maps.LatLng(51.555967, -0.279736);
var myOptions = {
zoom: 9,
center: latlng,
mapTypeId: google.maps.MapTypeId.TERRAIN
};
map = new google.maps.Map(document.getElementById("map"), myOptions);
var rendererOptions = {
map: map,
suppressMarkers: true,
};
directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
//Edinburgh to Eden Project
var point1 = new google.maps.LatLng(58.981386, -2.973751); //Orkney Rugby Football Club
var point2 = new google.maps.LatLng(55.881517, -4.342057); //Scotstoun Stadium
var point3 = new google.maps.LatLng(54.998080, -7.319812); //Guildhall St
var point4 = new google.maps.LatLng(54.563716, -5.942729); //Belfast Harlequins RFC
var point5 = new google.maps.LatLng(53.271057, -9.054253); //Hotel Spanish Arch
var point6 = new google.maps.LatLng(52.674222, -8.642515); //Thomond Park
var point7 = new google.maps.LatLng(53.291755, -3.713769); //Colwyn Leisure Centre
var point8 = new google.maps.LatLng(51.748180, -3.618567); //Glynneath Rugby Football Club
var wps = [{
location: point1
}, {
location: point2
}, {
location: point3
}, {
location: point4
}, {
location: point5
}, {
location: point6
}, {
location: point7
}, {
location: point8
}];
//START
var org = new google.maps.LatLng(55.945315, -3.205309); //EDINBURGH
//FINISH
var dest = new google.maps.LatLng(50.360130, -4.744717); //EDEN PROJECT
var request = {
origin: org,
destination: dest,
waypoints: wps,
travelMode: google.maps.DirectionsTravelMode.WALKING,
//unitSystem: google.maps.UnitSystem.IMPERIAL
};
directionsService = new google.maps.DirectionsService();
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
//SHOW ROUTE
directionsDisplay.setDirections(response);
//COPY POLY FROM DIRECTION SERVICE
var polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
var bounds = new google.maps.LatLngBounds();
var lengthMeters = 0;
var legs = response.routes[0].legs;
for (i = 0; i < legs.length; i++) {
var steps = legs[i].steps;
for (j = 0; j < steps.length; j++) {
var nextSegment = steps[j].path;
for (k = 0; k < nextSegment.length; k++) {
if (lengthMeters <= walked) {
polyline.getPath().push(nextSegment[k]);
if (polyline.getPath().getLength() > 1) {
var lastPt = polyline.getPath().getLength() - 1;
lengthMeters += google.maps.geometry.spherical.computeDistanceBetween(polyline.getPath().getAt(lastPt - 1), polyline.getPath().getAt(lastPt));
}
}
bounds.extend(nextSegment[k]);
}
}
}
polyline.setMap(map);
map.fitBounds(bounds);
//alert(walked);
//ADD MARKER TO NEW POLYLINE AT 'X' DISTANCE
createMarker(polyline.GetPointAtDistance(walked), "You are here", (Math.round(walked * METERS_TO_MILES * 10) / 10) + ' miles');
//GET THE TOTAL DISTANCE
var distance = 0;
//var METERS_TO_MILES = 0.000621371192;
for (i = 0; i < response.routes[0].legs.length; i++) {
//FOR EACH LEG GET THE DISTANCE AND ADD IT TO THE TOTAL
distance += parseFloat(response.routes[0].legs[i].distance.value);
}
//alert((Math.round( distance * METERS_TO_MILES * 10 ) / 10)+' miles');
//alert(distance); //METERS
} else if (status == google.maps.DirectionsStatus.MAX_WAYPOINTS_EXCEEDED) {
alert('Max waypoints exceeded');
} else {
alert('failed to get directions');
}
});
};
window.onload = function() {
initialize();
};
/*********************************************************************\
* *
* epolys.js by Mike Williams *
* updated to API v3 by Larry Ross *
* *
* A Google Maps API Extension *
* *
* Adds various Methods to google.maps.Polygon and google.maps.Polyline *
* *
* .Contains(latlng) returns true is the poly contains the specified *
* GLatLng *
* *
* .Area() returns the approximate area of a poly that is *
* not self-intersecting *
* *
* .Distance() returns the length of the poly path *
* *
* .Bounds() returns a GLatLngBounds that bounds the poly *
* *
* .GetPointAtDistance() returns a GLatLng at the specified distance *
* along the path. *
* The distance is specified in metres *
* Reurns null if the path is shorter than that *
* *
* .GetPointsAtDistance() returns an array of GLatLngs at the *
* specified interval along the path. *
* The distance is specified in metres *
* *
* .GetIndexAtDistance() returns the vertex number at the specified *
* distance along the path. *
* The distance is specified in metres *
* Returns null if the path is shorter than that *
* *
* .Bearing(v1?,v2?) returns the bearing between two vertices *
* if v1 is null, returns bearing from first to last *
* if v2 is null, returns bearing from v1 to next *
* *
* *
***********************************************************************
* *
* This Javascript is provided by Mike Williams *
* Blackpool Community Church Javascript Team *
* http://www.blackpoolchurch.org/ *
* http://econym.org.uk/gmap/ *
* *
* This work is licenced under a Creative Commons Licence *
* http://creativecommons.org/licenses/by/2.0/uk/ *
* *
***********************************************************************
* *
* Version 1.1 6-Jun-2007 *
* Version 1.2 1-Jul-2007 - fix: Bounds was omitting vertex zero *
* add: Bearing *
* Version 1.3 28-Nov-2008 add: GetPointsAtDistance() *
* Version 1.4 12-Jan-2009 fix: GetPointsAtDistance() *
* Version 3.0 11-Aug-2010 update to v3 *
* *
\*********************************************************************/
// === first support methods that don't (yet) exist in v3
google.maps.LatLng.prototype.distanceFrom = function(newLatLng) {
var EarthRadiusMeters = 6378137.0; // meters
var lat1 = this.lat();
var lon1 = this.lng();
var lat2 = newLatLng.lat();
var lon2 = newLatLng.lng();
var dLat = (lat2 - lat1) * Math.PI / 180;
var dLon = (lon2 - lon1) * Math.PI / 180;
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = EarthRadiusMeters * c;
return d;
}
google.maps.LatLng.prototype.latRadians = function() {
return this.lat() * Math.PI / 180;
}
google.maps.LatLng.prototype.lngRadians = function() {
return this.lng() * Math.PI / 180;
}
// === A method which returns the length of a path in metres ===
google.maps.Polygon.prototype.Distance = function() {
var dist = 0;
for (var i = 1; i < this.getPath().getLength(); i++) {
dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i - 1));
}
return dist;
}
// === A method which returns a GLatLng of a point a given distance along the path ===
// === Returns null if the path is shorter than the specified distance ===
google.maps.Polygon.prototype.GetPointAtDistance = function(metres) {
// some awkward special cases
if (metres == 0) return this.getPath().getAt(0);
if (metres < 0) return null;
if (this.getPath().getLength() < 2) return null;
var dist = 0;
var olddist = 0;
for (var i = 1;
(i < this.getPath().getLength() && dist < metres); i++) {
olddist = dist;
dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i - 1));
}
if (dist < metres) {
return null;
}
var p1 = this.getPath().getAt(i - 2);
var p2 = this.getPath().getAt(i - 1);
var m = (metres - olddist) / (dist - olddist);
return new google.maps.LatLng(p1.lat() + (p2.lat() - p1.lat()) * m, p1.lng() + (p2.lng() - p1.lng()) * m);
}
// === A method which returns an array of GLatLngs of points a given interval along the path ===
google.maps.Polygon.prototype.GetPointsAtDistance = function(metres) {
var next = metres;
var points = [];
// some awkward special cases
if (metres <= 0) return points;
var dist = 0;
var olddist = 0;
for (var i = 1;
(i < this.getPath().getLength()); i++) {
olddist = dist;
dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i - 1));
while (dist > next) {
var p1 = this.getPath().getAt(i - 1);
var p2 = this.getPath().getAt(i);
var m = (next - olddist) / (dist - olddist);
points.push(new google.maps.LatLng(p1.lat() + (p2.lat() - p1.lat()) * m, p1.lng() + (p2.lng() - p1.lng()) * m));
next += metres;
}
}
return points;
}
// === A method which returns the Vertex number at a given distance along the path ===
// === Returns null if the path is shorter than the specified distance ===
google.maps.Polygon.prototype.GetIndexAtDistance = function(metres) {
// some awkward special cases
if (metres == 0) return this.getPath().getAt(0);
if (metres < 0) return null;
var dist = 0;
var olddist = 0;
for (var i = 1;
(i < this.getPath().getLength() && dist < metres); i++) {
olddist = dist;
dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i - 1));
}
if (dist < metres) {
return null;
}
return i;
}
// === Copy all the above functions to GPolyline ===
google.maps.Polyline.prototype.Distance = google.maps.Polygon.prototype.Distance;
google.maps.Polyline.prototype.GetPointAtDistance = google.maps.Polygon.prototype.GetPointAtDistance;
google.maps.Polyline.prototype.GetPointsAtDistance = google.maps.Polygon.prototype.GetPointsAtDistance;
google.maps.Polyline.prototype.GetIndexAtDistance = google.maps.Polygon.prototype.GetIndexAtDistance;
html,
body,
#map,
#container {
height: 100%;
width: 100%;
background: #000;
padding: 0px;
margin: 0px;
}
<div id="container">
<div id="map"></div>
</div>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk">
</script>