'Google maps draw distance traveled polyline
I would like to draw a 'distance traveled' polyline over a preset route using V3 of the google maps API.
The polyline would need to run through multiple waypoints/legs.
I am currently using the DirectionsService to draw the complete route.
I am also using epolys.js to get the marker position for the distance traveled.
I am copying the complete route into a new polyline, but I would only like to copy the route up to the marker position.
Demo link: http://codepen.io/1983ron/pen/wKMVQr
And here's where I'm currently at with the JS
var geocoder;
var map;
var marker;
var gmarkers = [];
var METERS_TO_MILES = 0.000621371192;
var walked = (Math.round(670 * 1609.344));
//ICON
var iconImage = new google.maps.MarkerImage(
'http://www.ronnieatkinson.co.uk/clients/wc2015/maps/01/mapIcons/marker_red.png', //MARKER URL
new google.maps.Size(20, 34), //MARKER SIZE (WxH)
new google.maps.Point(0,0), //MARKER ORIGIN
new google.maps.Point(9, 34) //MARKER ANCHOR
);
//SHADOW
var iconShadow = new google.maps.MarkerImage(
'http://www.google.com/mapfiles/shadow50.png', //SHADOW URL
new google.maps.Size(37, 34), //SHADOW SIZE (WxH)
new google.maps.Point(0,0), //SHADOW ORIGIN
new google.maps.Point(9, 34) //SHADOW 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,
shadow: iconShadow,
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 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++) {
polyline.getPath().push(nextSegment[k]);
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(); };
Any help would be appreciated.
Solution 1:[1]
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);
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>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |