Google maps directions with more detailed points - google-maps

Currently if I search for a route using the DirectionsService it will return me a route with 1 or more legs, and each leg contains multiple steps. Each step seems to correspond to turns in a road. For example, if I drive 2km along street 1 and street1 is entirely a straight road, it will only contain two points (steps) that I can traverse over on that street. The first step will be where the journey along street 1 begins and the next step will be where the journey ends. This makes sense, but unfortunately I'm animating a journey along a map with a marker. This results in huge jumps on the map when there are long straight steps in the journey.
Is there any way to get more detailed steps so that the distance (by road) between each step will be more or less linear?

You can move along the straight line between vertices on the polyline.
See this example for one way to do that.
code snippet:
var map;
var directionDisplay;
var directionsService;
var stepDisplay;
var markerArray = [];
var position;
var marker = null;
var polyline = null;
var poly2 = null;
var speed = 0.000005, wait = 1;
var infowindow = null;
var myPano;
var panoClient;
var nextPanoId;
var timerHandle = null;
function createMarker(latlng, label, html) {
// alert("createMarker("+latlng+","+label+","+html+","+color+")");
var contentString = '<b>'+label+'</b><br>'+html;
var marker = new google.maps.Marker({
position: latlng,
map: map,
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() {
infowindow = new google.maps.InfoWindow(
{
size: new google.maps.Size(150,50)
});
// Instantiate a directions service.
directionsService = new google.maps.DirectionsService();
// Create a map and center it on Manhattan.
var myOptions = {
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
address = 'new york'
geocoder = new google.maps.Geocoder();
geocoder.geocode( { 'address': address}, function(results, status) {
map.setCenter(results[0].geometry.location);
});
// Create a renderer for directions and bind it to the map.
var rendererOptions = {
map: map
}
directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
// Instantiate an info window to hold step text.
stepDisplay = new google.maps.InfoWindow();
polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
poly2 = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
}
var steps = []
function calcRoute(){
if (timerHandle) { clearTimeout(timerHandle); }
if (marker) { marker.setMap(null);}
polyline.setMap(null);
poly2.setMap(null);
directionsDisplay.setMap(null);
polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
poly2 = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
// Create a renderer for directions and bind it to the map.
var rendererOptions = {
map: map
}
directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
var start = document.getElementById("start").value;
var end = document.getElementById("end").value;
var travelMode = google.maps.DirectionsTravelMode.DRIVING
var request = {
origin: start,
destination: end,
travelMode: travelMode
};
// Route the directions and pass the response to a
// function to create markers for each step.
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK){
directionsDisplay.setDirections(response);
var bounds = new google.maps.LatLngBounds();
var route = response.routes[0];
startLocation = new Object();
endLocation = new Object();
// For each route, display summary information.
var path = response.routes[0].overview_path;
var legs = response.routes[0].legs;
for (i=0;i<legs.length;i++) {
if (i == 0) {
startLocation.latlng = legs[i].start_location;
startLocation.address = legs[i].start_address;
// marker = google.maps.Marker({map:map,position: startLocation.latlng});
marker = createMarker(legs[i].start_location,"start",legs[i].start_address,"green");
}
endLocation.latlng = legs[i].end_location;
endLocation.address = legs[i].end_address;
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);
// createMarker(endLocation.latlng,"end",endLocation.address,"red");
map.setZoom(18);
startAnimation();
}
});
}
var step = 50; // 5; // metres
var tick = 100; // milliseconds
var eol;
var k=0;
var stepnum=0;
var speed = "";
var lastVertex = 1;
//=============== animation functions ======================
function updatePoly(d) {
// Spawn a new polyline every 20 vertices, because updating a 100-vertex poly is too slow
if (poly2.getPath().getLength() > 20) {
poly2=new google.maps.Polyline([polyline.getPath().getAt(lastVertex-1)]);
// map.addOverlay(poly2)
}
if (polyline.GetIndexAtDistance(d) < lastVertex+2) {
if (poly2.getPath().getLength()>1) {
poly2.getPath().removeAt(poly2.getPath().getLength()-1)
}
poly2.getPath().insertAt(poly2.getPath().getLength(),polyline.GetPointAtDistance(d));
} else {
poly2.getPath().insertAt(poly2.getPath().getLength(),endLocation.latlng);
}
}
function animate(d) {
// alert("animate("+d+")");
if (d>eol) {
map.panTo(endLocation.latlng);
marker.setPosition(endLocation.latlng);
return;
}
var p = polyline.GetPointAtDistance(d);
map.panTo(p);
marker.setPosition(p);
updatePoly(d);
timerHandle = setTimeout("animate("+(d+step)+")", tick);
}
function startAnimation() {
eol=polyline.Distance();
map.setCenter(polyline.getPath().getAt(0));
// map.addOverlay(new google.maps.Marker(polyline.getAt(0),G_START_ICON));
// map.addOverlay(new GMarker(polyline.getVertex(polyline.getVertexCount()-1),G_END_ICON));
// marker = new google.maps.Marker({location:polyline.getPath().getAt(0)} /* ,{icon:car} */);
// map.addOverlay(marker);
poly2 = new google.maps.Polyline({path: [polyline.getPath().getAt(0)], strokeColor:"#0000FF", strokeWeight:10});
// map.addOverlay(poly2);
setTimeout("animate(50)",2000); // Allow time for the initial map display
}
//=============== ~animation funcitons =====================
// from epolys_v3.js
// http://www.geocodezip.com/scripts/v3_epoly.js
/*********************************************************************\
* .Distance() returns the length of the poly path *
* .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 *
* .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 *
***********************************************************************/
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;
}
// === A method which returns the length of a path in metres ===
google.maps.Polyline.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.Polyline.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 the Vertex number at a given distance along the path ===
// === Returns null if the path is shorter than the specified distance ===
google.maps.Polyline.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;
}
html{height:100%;}
body{height:100%;margin:0px;font-family: Helvetica,Arial;}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no"/>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Google Maps JavaScript API v3 Example: Directions Complex</title>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<script type ="text/javascript" src="scripts/v3_epoly.js"></script>
</head>
<body onload="initialize()">
<div id="tools">
start:
<input type="text" name="start" id="start" value="union square, NY"/>
end:
<input type="text" name="end" id="end" value="times square, NY"/>
<input type="submit" onclick="calcRoute();"/>
</div>
<div id="map_canvas" style="width:100%;height:100%;"></div>
</body>
</html>

Related

How can I determine the location along a given route using mileage? [duplicate]

This question already has an answer here:
latLng from polyLine Percentage
(1 answer)
Closed 2 years ago.
For example, given a route between Miami and New York (driving), I'd like to figure out the lat/long of a location along that same route after some number of miles.
Does Google's API provide anyway of finding a location using a given route and asking for a location some miles into it?
related questions:
Get location after travel x distance on a Google Maps route
latLng from polyLine Percentage
Midpoint of route in google maps
Uses
http://www.geocodezip.com/scripts/v3_epoly.js
A 3rd party library written for the Google Maps Javascript API v2 by Mike Williams, ported to v3.Documentation for the v2 version
Has this method:
.GetPointAtDistance() returns a LatLng at the specified distance
along the path.
The distance is specified in metres
Returns null if the path is shorter than that
live example
code snippet:
var directionDisplay;
var directionsService = new google.maps.DirectionsService();
var map;
var polyline = null;
var marker;
var infowindow;
function createMarker(latlng, label, html) {
var contentString = '<b>' + label + '</b><br>' + html;
var marker = new google.maps.Marker({
position: latlng,
map: map,
title: label,
zIndex: Math.round(latlng.lat() * -100000) << 5,
contentString: contentString
});
marker.myname = label;
// gmarkers.push(marker);
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(this.contentString);
infowindow.open(map, marker);
});
return marker;
}
var myLatLng = null;
var lat;
var lng;
var zoom = 2;
var maptype;
function initialize() {
infowindow = new google.maps.InfoWindow();
myLatLng = new google.maps.LatLng(37.422104808, -122.0838851);
maptype = google.maps.MapTypeId.ROADMAP;
var myOptions = {
zoom: zoom,
center: myLatLng,
mapTypeId: maptype
};
directionsDisplay = new google.maps.DirectionsRenderer({
suppressMarkers: true
});
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
directionsDisplay.setMap(map);
calcRoute();
}
function calcRoute() {
var start = document.getElementById("start").value;
var end = document.getElementById("end").value;
var travelMode = google.maps.DirectionsTravelMode.DRIVING
var request = {
origin: start,
destination: end,
travelMode: travelMode
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
polyline.setPath([]);
var bounds = new google.maps.LatLngBounds();
startLocation = new Object();
endLocation = new Object();
directionsDisplay.setDirections(response);
var route = response.routes[0];
var summaryPanel = document.getElementById("directions_panel");
summaryPanel.innerHTML = "";
// For each route, display summary information.
var path = response.routes[0].overview_path;
var legs = response.routes[0].legs;
for (i = 0; i < legs.length; i++) {
if (i == 0) {
startLocation.latlng = legs[i].start_location;
startLocation.address = legs[i].start_address;
}
endLocation.latlng = legs[i].end_location;
endLocation.address = legs[i].end_address;
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);
computeTotalDistance(response);
putMarkerOnRoute(parseFloat(document.getElementById('distance').value));
} else {
alert("directions response " + status);
}
});
}
var totalDist = 0;
var totalTime = 0;
function computeTotalDistance(result) {
totalDist = 0;
totalTime = 0;
var myroute = result.routes[0];
for (i = 0; i < myroute.legs.length; i++) {
totalDist += myroute.legs[i].distance.value;
totalTime += myroute.legs[i].duration.value;
}
totalDist = totalDist / 1000.
document.getElementById("total").innerHTML = "total distance is: " + totalDist + " km<br>total time is: " + (totalTime / 60).toFixed(2) + " minutes<br>average speed is: " + (totalDist / (totalTime / 3600)).toFixed(2) + " kph";
document.getElementById("totalTime").value = (totalTime / 60.).toFixed(2);
}
function putMarkerOnRoute(distance) {
if (distance > totalDist)
distance = totalDist;
time = distance / totalDist * totalTime / 60;
if (!marker) {
marker = createMarker(polyline.GetPointAtDistance(distance * 1000), "distance: " + distance, "marker");
} else {
marker.setPosition(polyline.GetPointAtDistance(distance * 1000));
marker.setTitle("time:" + time.toFixed(2));
marker.contentString = "<b>time: " + time.toFixed(2) + "</b><br>distance: " + (distance).toFixed(2) + " km<br>marker";
google.maps.event.trigger(marker, "click");
}
}
google.maps.event.addDomListener(window, 'load', initialize);
// from v3_epoly.js
// === 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.Polyline.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 += google.maps.geometry.spherical.computeDistanceBetween(this.getPath().getAt(i), 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);
}
html {
height: 100%
}
body {
height: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&libraries=geometry&v=weekly"></script>
<div id="tools">
start:
<input type="text" name="start" id="start" value="Hyderabad" /> end:
<input type="text" name="end" id="end" value="Bangalore" />
<input type="submit" onclick="calcRoute();" /><br /> distance:
<input type="text" name="distance" id="distance" value="100" />
<input type="submit" onclick="putMarkerOnRoute(parseFloat(document.getElementById('distance').value));" /> total time:<input type="text" name="totalTime" id="totalTime" value="0" />
</div>
<div id="map_canvas" style="float:left;width:70%;height:80%;"></div>
<div id="control_panel" style="float:right;width:30%;text-align:left;padding-top:20px">
<div id="directions_panel" style="margin:20px;background-color:#FFEE77;"></div>
<div id="total"></div>

Google map api get closest place to me in my address

I'm writing an site that shows the our stores near an address. I have the list of latitude and longitude locations of each store from a service.I found my position and marked the stores.But i cant found closest store to my location.Can u help me? :)
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
center: new google.maps.LatLng(40.188528, 29.060964),
zoom: 10
});
var infoWindow = new google.maps.InfoWindow;
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var pos = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
infoWindow.setPosition(pos);
infoWindow.setContent('Location found.');
infoWindow.open(map);
}, function() {
handleLocationError(true, infoWindow, map.getCenter());
});
} else {
// Browser doesn't support Geolocation
handleLocationError(false, infoWindow, map.getCenter());
}
downloadUrl('locations.xml', function(data) {
var xml = data.responseXML;
var markers = xml.documentElement.getElementsByTagName('marker');
Array.prototype.forEach.call(markers, function(markerElem) {
var id = markerElem.getAttribute('id');
var name = markerElem.getAttribute('name');
var address = markerElem.getAttribute('address');
var type = markerElem.getAttribute('type');
var point = new google.maps.LatLng(
parseFloat(markerElem.getAttribute('lat')),
parseFloat(markerElem.getAttribute('lng')));
var infowincontent = document.createElement('div');
var strong = document.createElement('strong');
strong.textContent = name
infowincontent.appendChild(strong);
infowincontent.appendChild(document.createElement('br'));
var text = document.createElement('text');
text.textContent = address
infowincontent.appendChild(text);
var icon = customLabel[type] || {};
var marker = new google.maps.Marker({
map: map,
position: point,
label: icon.label
});
marker.addListener('click', function() {
infoWindow.setContent(infowincontent);
infoWindow.open(map, marker);
});
});
});
}
<markers>
<marker id="1" name="Pronet" address="Altınşehir Mahallesi, Muammer Aksoy Cd. No:45, 16120 Nilüfer/Bursa" lat="40.227127" lng="28.924318" type="Ajans"/>
<marker id="2" name="Nette" address="Fethiye Mahallesi, Bursa Cd No:1, 16140 Başiskele/Bursa" lat="40.227173" lng="28.979762" type="Ajans"/>
<marker id="3" name="Alivre" address="Osmangazi Mahallesi, Altıparmak Caddesi, No:86 Stad İş Merkezi K:6 D:15, 16050 Osmangazi/Bursa" lat="40.190341" lng="29.050251" type="Ajans"/>
You can use this function in order to find the closest marker around your position:
function rad(x) { return x * Math.PI / 180; }
function find_closest_marker(position) {
var lat = position.lat;
var lng = position.lng;
var R = 6371; // radius of earth in km
var distances = [];
var closest = -1;
for (i = 0; i < gmarkers.length; i++) {
if (gmarkers[i]) {
var mlat = gmarkers[i].position.lat();
var mlng = gmarkers[i].position.lng();
var dLat = rad(mlat - lat);
var dLong = rad(mlng - lng);
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(rad(lat)) * Math.cos(rad(lat)) * Math.sin(dLong / 2) * Math.sin(dLong / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
distances[i] = d;
if (closest == -1 || d < distances[closest]) {
closest = i;
}
}
}
return (closest);
}
However, when you initialize your markers you may put them in an array markers like this:
markers.push (newMarker);
You may use the function like this:
var closestMarker = find_closest_marker (yourPosition);
closestMarker=markers[closestMarker];
The function find_closest_marker return the case of the nearest in the array markers.
Tell me if you have some questions.

Make a marker face the correct direction of movement as it moves on the map [duplicate]

I'd like to know if it possible to change the marker orientation according the path drawn on the map. Here is a example:
As you can see the marker is a car (with front bumper and tail lights). I'd like to orientate the car in the direction the path is going (in this exemple orientate the car around 45 degrees to the right).
I'm using DirectionsService to draw the path and I have a random number of point. Sometime only one, sometime 10 points. I'm adding the markers before drawing the paths. Here is how I'm drawing the path:
// Intialize the Path Array
var path = new google.maps.MVCArray();
// Intialise the Direction Service
var service = new google.maps.DirectionsService();
// Set the Path Stroke Color
var poly = new google.maps.Polyline({ map: gmap, strokeColor: '#dd0000' }); // #4986E7
// Draw the path for this vehicle
for (var i = 0; i < pathPoints.length; i++) {
if ((i + 1) < pathPoints.length) {
var src = pathPoints[i];
var des = pathPoints[i + 1];
path.push(src);
poly.setPath(path);
service.route({
origin: src,
destination: des,
travelMode: google.maps.DirectionsTravelMode.DRIVING
}, function (result, status) {
if (status == google.maps.DirectionsStatus.OK) {
for (var i = 0, len = result.routes[0].overview_path.length; i < len; i++){
path.push(result.routes[0].overview_path[i]);
}
}
});
}
}
Is it possible to do that?
One option is to translate your icon to SVG then use the Symbol rotation property to align it with the road (you can do the same with a PNG image by either making a large number of copies of the icon rotated by a degree or two, or by making a custom icon that allows you to rotate the PNG icon arbitrarily)
marker.setPosition(p);
var heading = google.maps.geometry.spherical.computeHeading(lastPosn,p);
icon.rotation = heading;
marker.setIcon(icon);
proof of concept fiddle
code snippet:
var map;
var directionDisplay;
var directionsService;
var stepDisplay;
var markerArray = [];
var position;
var marker = null;
var polyline = null;
var poly2 = null;
var speed = 0.000005,
wait = 1;
var infowindow = null;
var timerHandle = null;
function createMarker(latlng, label, html) {
var contentString = '<b>' + label + '</b><br>' + html;
var marker = new google.maps.Marker({
position: latlng,
map: map,
title: label,
zIndex: Math.round(latlng.lat() * -100000) << 5
});
marker.myname = label;
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(contentString);
infowindow.open(map, marker);
});
return marker;
}
function initialize() {
infowindow = new google.maps.InfoWindow({
size: new google.maps.Size(150, 50)
});
// Instantiate a directions service.
directionsService = new google.maps.DirectionsService();
// Create a map and center it on Manhattan.
var myOptions = {
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
address = 'new york';
geocoder = new google.maps.Geocoder();
geocoder.geocode({
'address': address
}, function(results, status) {
map.setCenter(results[0].geometry.location);
});
// Create a renderer for directions and bind it to the map.
var rendererOptions = {
map: map
};
directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
// Instantiate an info window to hold step text.
stepDisplay = new google.maps.InfoWindow();
polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
poly2 = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
}
var steps = [];
function calcRoute() {
if (timerHandle) {
clearTimeout(timerHandle);
}
if (marker) {
marker.setMap(null);
}
polyline.setMap(null);
poly2.setMap(null);
directionsDisplay.setMap(null);
polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
poly2 = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
// Create a renderer for directions and bind it to the map.
var rendererOptions = {
map: map
};
directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
var start = document.getElementById("start").value;
var end = document.getElementById("end").value;
var travelMode = google.maps.DirectionsTravelMode.DRIVING;
var request = {
origin: start,
destination: end,
travelMode: travelMode
};
// Route the directions and pass the response to a
// function to create markers for each step.
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
var bounds = new google.maps.LatLngBounds();
var route = response.routes[0];
startLocation = new Object();
endLocation = new Object();
// For each route, display summary information.
var path = response.routes[0].overview_path;
var legs = response.routes[0].legs;
for (i = 0; i < legs.length; i++) {
if (i === 0) {
startLocation.latlng = legs[i].start_location;
startLocation.address = legs[i].start_address;
// marker = createMarker(legs[i].start_location, "start", legs[i].start_address, "green");
}
endLocation.latlng = legs[i].end_location;
endLocation.address = legs[i].end_address;
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);
map.setZoom(18);
startAnimation();
}
});
}
var step = 50; // 5; // metres
var tick = 100; // milliseconds
var eol;
var k = 0;
var stepnum = 0;
var speed = "";
var lastVertex = 1;
//=============== animation functions ======================
function updatePoly(d) {
// Spawn a new polyline every 20 vertices, because updating a 100-vertex poly is too slow
if (poly2.getPath().getLength() > 20) {
poly2 = new google.maps.Polyline([polyline.getPath().getAt(lastVertex - 1)]);
// map.addOverlay(poly2)
}
if (polyline.GetIndexAtDistance(d) < lastVertex + 2) {
if (poly2.getPath().getLength() > 1) {
poly2.getPath().removeAt(poly2.getPath().getLength() - 1);
}
poly2.getPath().insertAt(poly2.getPath().getLength(), polyline.GetPointAtDistance(d));
} else {
poly2.getPath().insertAt(poly2.getPath().getLength(), endLocation.latlng);
}
}
function animate(d) {
if (d > eol) {
map.panTo(endLocation.latlng);
marker.setPosition(endLocation.latlng);
return;
}
var p = polyline.GetPointAtDistance(d);
map.panTo(p);
var lastPosn = marker.getPosition();
marker.setPosition(p);
var heading = google.maps.geometry.spherical.computeHeading(lastPosn, p);
icon.rotation = heading;
marker.setIcon(icon);
updatePoly(d);
timerHandle = setTimeout("animate(" + (d + step) + ")", tick);
}
function startAnimation() {
eol = polyline.Distance();
map.setCenter(polyline.getPath().getAt(0));
marker = new google.maps.Marker({
position: polyline.getPath().getAt(0),
map: map,
icon: icon
});
poly2 = new google.maps.Polyline({
path: [polyline.getPath().getAt(0)],
strokeColor: "#0000FF",
strokeWeight: 10
});
// map.addOverlay(poly2);
setTimeout("animate(50)", 2000); // Allow time for the initial map display
}
google.maps.event.addDomListener(window, 'load', initialize);
//=============== ~animation funcitons =====================
var car = "M17.402,0H5.643C2.526,0,0,3.467,0,6.584v34.804c0,3.116,2.526,5.644,5.643,5.644h11.759c3.116,0,5.644-2.527,5.644-5.644 V6.584C23.044,3.467,20.518,0,17.402,0z M22.057,14.188v11.665l-2.729,0.351v-4.806L22.057,14.188z M20.625,10.773 c-1.016,3.9-2.219,8.51-2.219,8.51H4.638l-2.222-8.51C2.417,10.773,11.3,7.755,20.625,10.773z M3.748,21.713v4.492l-2.73-0.349 V14.502L3.748,21.713z M1.018,37.938V27.579l2.73,0.343v8.196L1.018,37.938z M2.575,40.882l2.218-3.336h13.771l2.219,3.336H2.575z M19.328,35.805v-7.872l2.729-0.355v10.048L19.328,35.805z";
var icon = {
path: car,
scale: .7,
strokeColor: 'white',
strokeWeight: .10,
fillOpacity: 1,
fillColor: '#404040',
offset: '5%',
// rotation: parseInt(heading[i]),
anchor: new google.maps.Point(10, 25) // orig 10,50 back of car, 10,0 front of car, 10,25 center of car
};
/*********************************************************************\
* *
* 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 {
height: 100%;
}
body {
height: 100%;
margin: 0px;
font-family: Helvetica, Arial;
}
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<div id="tools">start:
<input type="text" name="start" id="start" value="union square, NY" />end:
<input type="text" name="end" id="end" value="times square, NY" />
<input type="submit" onclick="calcRoute();" />
</div>
<div id="map_canvas" style="width:100%;height:90%;"></div>
Easiest solution for this (FOR GOOGLE MAPS) :
var rotationAngle = google.maps.geometry.spherical.computeHeading(oldLatlng, currentlatlng);
$('img[src="path_to_marker_icon_image"]').css({
transform: "rotate(" + rotationAngle + "deg)"
});
You can use it easily do with the help of the 'GMSMapView' Delegates method.
var lastMapBearing: CLLocationDirection?
var lastUserBearing: CLLocationDirection?
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
lastMapBearing = position.bearing
driverMarker?.rotation = (lastUserBearing ?? 0) - lastMapBearing!
}
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
lastUserBearing = newHeading.trueHeading
driverMarker?.rotation = lastUserBearing! - (lastMapBearing ?? 0)
}
Make sure you can assign the delegate itSelf.

Change Google map marker orientation according path direction

I'd like to know if it possible to change the marker orientation according the path drawn on the map. Here is a example:
As you can see the marker is a car (with front bumper and tail lights). I'd like to orientate the car in the direction the path is going (in this exemple orientate the car around 45 degrees to the right).
I'm using DirectionsService to draw the path and I have a random number of point. Sometime only one, sometime 10 points. I'm adding the markers before drawing the paths. Here is how I'm drawing the path:
// Intialize the Path Array
var path = new google.maps.MVCArray();
// Intialise the Direction Service
var service = new google.maps.DirectionsService();
// Set the Path Stroke Color
var poly = new google.maps.Polyline({ map: gmap, strokeColor: '#dd0000' }); // #4986E7
// Draw the path for this vehicle
for (var i = 0; i < pathPoints.length; i++) {
if ((i + 1) < pathPoints.length) {
var src = pathPoints[i];
var des = pathPoints[i + 1];
path.push(src);
poly.setPath(path);
service.route({
origin: src,
destination: des,
travelMode: google.maps.DirectionsTravelMode.DRIVING
}, function (result, status) {
if (status == google.maps.DirectionsStatus.OK) {
for (var i = 0, len = result.routes[0].overview_path.length; i < len; i++){
path.push(result.routes[0].overview_path[i]);
}
}
});
}
}
Is it possible to do that?
One option is to translate your icon to SVG then use the Symbol rotation property to align it with the road (you can do the same with a PNG image by either making a large number of copies of the icon rotated by a degree or two, or by making a custom icon that allows you to rotate the PNG icon arbitrarily)
marker.setPosition(p);
var heading = google.maps.geometry.spherical.computeHeading(lastPosn,p);
icon.rotation = heading;
marker.setIcon(icon);
proof of concept fiddle
code snippet:
var map;
var directionDisplay;
var directionsService;
var stepDisplay;
var markerArray = [];
var position;
var marker = null;
var polyline = null;
var poly2 = null;
var speed = 0.000005,
wait = 1;
var infowindow = null;
var timerHandle = null;
function createMarker(latlng, label, html) {
var contentString = '<b>' + label + '</b><br>' + html;
var marker = new google.maps.Marker({
position: latlng,
map: map,
title: label,
zIndex: Math.round(latlng.lat() * -100000) << 5
});
marker.myname = label;
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(contentString);
infowindow.open(map, marker);
});
return marker;
}
function initialize() {
infowindow = new google.maps.InfoWindow({
size: new google.maps.Size(150, 50)
});
// Instantiate a directions service.
directionsService = new google.maps.DirectionsService();
// Create a map and center it on Manhattan.
var myOptions = {
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
address = 'new york';
geocoder = new google.maps.Geocoder();
geocoder.geocode({
'address': address
}, function(results, status) {
map.setCenter(results[0].geometry.location);
});
// Create a renderer for directions and bind it to the map.
var rendererOptions = {
map: map
};
directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
// Instantiate an info window to hold step text.
stepDisplay = new google.maps.InfoWindow();
polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
poly2 = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
}
var steps = [];
function calcRoute() {
if (timerHandle) {
clearTimeout(timerHandle);
}
if (marker) {
marker.setMap(null);
}
polyline.setMap(null);
poly2.setMap(null);
directionsDisplay.setMap(null);
polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
poly2 = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
// Create a renderer for directions and bind it to the map.
var rendererOptions = {
map: map
};
directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
var start = document.getElementById("start").value;
var end = document.getElementById("end").value;
var travelMode = google.maps.DirectionsTravelMode.DRIVING;
var request = {
origin: start,
destination: end,
travelMode: travelMode
};
// Route the directions and pass the response to a
// function to create markers for each step.
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
var bounds = new google.maps.LatLngBounds();
var route = response.routes[0];
startLocation = new Object();
endLocation = new Object();
// For each route, display summary information.
var path = response.routes[0].overview_path;
var legs = response.routes[0].legs;
for (i = 0; i < legs.length; i++) {
if (i === 0) {
startLocation.latlng = legs[i].start_location;
startLocation.address = legs[i].start_address;
// marker = createMarker(legs[i].start_location, "start", legs[i].start_address, "green");
}
endLocation.latlng = legs[i].end_location;
endLocation.address = legs[i].end_address;
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);
map.setZoom(18);
startAnimation();
}
});
}
var step = 50; // 5; // metres
var tick = 100; // milliseconds
var eol;
var k = 0;
var stepnum = 0;
var speed = "";
var lastVertex = 1;
//=============== animation functions ======================
function updatePoly(d) {
// Spawn a new polyline every 20 vertices, because updating a 100-vertex poly is too slow
if (poly2.getPath().getLength() > 20) {
poly2 = new google.maps.Polyline([polyline.getPath().getAt(lastVertex - 1)]);
// map.addOverlay(poly2)
}
if (polyline.GetIndexAtDistance(d) < lastVertex + 2) {
if (poly2.getPath().getLength() > 1) {
poly2.getPath().removeAt(poly2.getPath().getLength() - 1);
}
poly2.getPath().insertAt(poly2.getPath().getLength(), polyline.GetPointAtDistance(d));
} else {
poly2.getPath().insertAt(poly2.getPath().getLength(), endLocation.latlng);
}
}
function animate(d) {
if (d > eol) {
map.panTo(endLocation.latlng);
marker.setPosition(endLocation.latlng);
return;
}
var p = polyline.GetPointAtDistance(d);
map.panTo(p);
var lastPosn = marker.getPosition();
marker.setPosition(p);
var heading = google.maps.geometry.spherical.computeHeading(lastPosn, p);
icon.rotation = heading;
marker.setIcon(icon);
updatePoly(d);
timerHandle = setTimeout("animate(" + (d + step) + ")", tick);
}
function startAnimation() {
eol = polyline.Distance();
map.setCenter(polyline.getPath().getAt(0));
marker = new google.maps.Marker({
position: polyline.getPath().getAt(0),
map: map,
icon: icon
});
poly2 = new google.maps.Polyline({
path: [polyline.getPath().getAt(0)],
strokeColor: "#0000FF",
strokeWeight: 10
});
// map.addOverlay(poly2);
setTimeout("animate(50)", 2000); // Allow time for the initial map display
}
google.maps.event.addDomListener(window, 'load', initialize);
//=============== ~animation funcitons =====================
var car = "M17.402,0H5.643C2.526,0,0,3.467,0,6.584v34.804c0,3.116,2.526,5.644,5.643,5.644h11.759c3.116,0,5.644-2.527,5.644-5.644 V6.584C23.044,3.467,20.518,0,17.402,0z M22.057,14.188v11.665l-2.729,0.351v-4.806L22.057,14.188z M20.625,10.773 c-1.016,3.9-2.219,8.51-2.219,8.51H4.638l-2.222-8.51C2.417,10.773,11.3,7.755,20.625,10.773z M3.748,21.713v4.492l-2.73-0.349 V14.502L3.748,21.713z M1.018,37.938V27.579l2.73,0.343v8.196L1.018,37.938z M2.575,40.882l2.218-3.336h13.771l2.219,3.336H2.575z M19.328,35.805v-7.872l2.729-0.355v10.048L19.328,35.805z";
var icon = {
path: car,
scale: .7,
strokeColor: 'white',
strokeWeight: .10,
fillOpacity: 1,
fillColor: '#404040',
offset: '5%',
// rotation: parseInt(heading[i]),
anchor: new google.maps.Point(10, 25) // orig 10,50 back of car, 10,0 front of car, 10,25 center of car
};
/*********************************************************************\
* *
* 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 {
height: 100%;
}
body {
height: 100%;
margin: 0px;
font-family: Helvetica, Arial;
}
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<div id="tools">start:
<input type="text" name="start" id="start" value="union square, NY" />end:
<input type="text" name="end" id="end" value="times square, NY" />
<input type="submit" onclick="calcRoute();" />
</div>
<div id="map_canvas" style="width:100%;height:90%;"></div>
Easiest solution for this (FOR GOOGLE MAPS) :
var rotationAngle = google.maps.geometry.spherical.computeHeading(oldLatlng, currentlatlng);
$('img[src="path_to_marker_icon_image"]').css({
transform: "rotate(" + rotationAngle + "deg)"
});
You can use it easily do with the help of the 'GMSMapView' Delegates method.
var lastMapBearing: CLLocationDirection?
var lastUserBearing: CLLocationDirection?
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
lastMapBearing = position.bearing
driverMarker?.rotation = (lastUserBearing ?? 0) - lastMapBearing!
}
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
lastUserBearing = newHeading.trueHeading
driverMarker?.rotation = lastUserBearing! - (lastMapBearing ?? 0)
}
Make sure you can assign the delegate itSelf.

Google Maps V3 - waypoints + infowindow with random text

I'm using this example to set up a route with more then 8 markers.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>Google Maps JavaScript API v3 Example: Directions Waypoints</title>
<style>
#map{
width: 100%;
height: 450px;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>
<script>
jQuery(function() {
var stops = [
{"Geometry":{"Latitude":52.1615470947258,"Longitude":20.80514430999756}},
{"Geometry":{"Latitude":52.15991486090931,"Longitude":20.804049968719482}},
{"Geometry":{"Latitude":52.15772967999426,"Longitude":20.805788040161133}},
{"Geometry":{"Latitude":52.15586034371232,"Longitude":20.80460786819458}},
{"Geometry":{"Latitude":52.15923693975469,"Longitude":20.80113172531128}},
{"Geometry":{"Latitude":52.159849043774074, "Longitude":20.791990756988525}},
{"Geometry":{"Latitude":52.15986220720892,"Longitude":20.790467262268066}},
{"Geometry":{"Latitude":52.16202095784738,"Longitude":20.7806396484375}},
{"Geometry":{"Latitude":52.16088894313116,"Longitude":20.77737808227539}},
{"Geometry":{"Latitude":52.15255590234335,"Longitude":20.784244537353516}},
{"Geometry":{"Latitude":52.14747369312591,"Longitude":20.791218280792236}},
{"Geometry":{"Latitude":52.14963304460396,"Longitude":20.79387903213501}}
] ;
var map = new window.google.maps.Map(document.getElementById("map"));
// new up complex objects before passing them around
var directionsDisplay = new window.google.maps.DirectionsRenderer();
var directionsService = new window.google.maps.DirectionsService();
Tour_startUp(stops);
window.tour.loadMap(map, directionsDisplay);
window.tour.fitBounds(map);
if (stops.length > 1)
window.tour.calcRoute(directionsService, directionsDisplay);
});
function Tour_startUp(stops) {
if (!window.tour) window.tour = {
updateStops: function (newStops) {
stops = newStops;
},
// map: google map object
// directionsDisplay: google directionsDisplay object (comes in empty)
loadMap: function (map, directionsDisplay) {
var myOptions = {
zoom: 13,
center: new window.google.maps.LatLng(51.507937, -0.076188), // default to London
mapTypeId: window.google.maps.MapTypeId.ROADMAP
};
map.setOptions(myOptions);
directionsDisplay.setMap(map);
},
fitBounds: function (map) {
var bounds = new window.google.maps.LatLngBounds();
// extend bounds for each record
jQuery.each(stops, function (key, val) {
var myLatlng = new window.google.maps.LatLng(val.Geometry.Latitude, val.Geometry.Longitude);
bounds.extend(myLatlng);
});
map.fitBounds(bounds);
},
calcRoute: function (directionsService, directionsDisplay) {
var batches = [];
var itemsPerBatch = 10; // google API max = 10 - 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: new window.google.maps.LatLng(stops[j].Geometry.Latitude, stops[j].Geometry.Longitude),
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--;
}
// now we should have a 2 dimensional array with a list of a list of waypoints
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,
travelMode: window.google.maps.TravelMode.WALKING
};
(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);
}
}
});
})(k);
}
}
};
}
</script>
</head>
<body>
<div id="map"></div>
</body>
</html>
It works like a charm but I have problem to setup an infowindow for each waypoint. I would like to leave markers names like A , B ,C or generate it in otherway but to keep A,B,C or 1,2,3 pattern and I want to add infowindow to each marker which contains title text and link.
I was trying to find any info or examples but nothing works. Maybe someone have and idea how to easily add infowindow to this code.
Here is an example that draws the directions from scratch with custom markers and infowindows:
Example
If you use the suppressInfoWindows option on the DirectionsRenderer, you can just use the part of it that creates the markers and their associated infowindows.
Working example based on your code
Code:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>Google Maps JavaScript API v3 Example: Directions Waypoints</title>
<style>
#map{
width: 100%;
height: 450px;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js"></script>
<script>
jQuery(function() {
var stops = [
{"Geometry":{"Latitude":52.1615470947258,"Longitude":20.80514430999756}},
{"Geometry":{"Latitude":52.15991486090931,"Longitude":20.804049968719482}},
{"Geometry":{"Latitude":52.15772967999426,"Longitude":20.805788040161133}},
{"Geometry":{"Latitude":52.15586034371232,"Longitude":20.80460786819458}},
{"Geometry":{"Latitude":52.15923693975469,"Longitude":20.80113172531128}},
{"Geometry":{"Latitude":52.159849043774074, "Longitude":20.791990756988525}},
{"Geometry":{"Latitude":52.15986220720892,"Longitude":20.790467262268066}},
{"Geometry":{"Latitude":52.16202095784738,"Longitude":20.7806396484375}},
{"Geometry":{"Latitude":52.16088894313116,"Longitude":20.77737808227539}},
{"Geometry":{"Latitude":52.15255590234335,"Longitude":20.784244537353516}},
{"Geometry":{"Latitude":52.14747369312591,"Longitude":20.791218280792236}},
{"Geometry":{"Latitude":52.14963304460396,"Longitude":20.79387903213501}}
] ;
var map = new window.google.maps.Map(document.getElementById("map"));
// new up complex objects before passing them around
var directionsDisplay = new window.google.maps.DirectionsRenderer({suppressMarkers: true});
var directionsService = new window.google.maps.DirectionsService();
Tour_startUp(stops);
window.tour.loadMap(map, directionsDisplay);
window.tour.fitBounds(map);
if (stops.length > 1)
window.tour.calcRoute(directionsService, directionsDisplay);
});
function Tour_startUp(stops) {
if (!window.tour) window.tour = {
updateStops: function (newStops) {
stops = newStops;
},
// map: google map object
// directionsDisplay: google directionsDisplay object (comes in empty)
loadMap: function (map, directionsDisplay) {
var myOptions = {
zoom: 13,
center: new window.google.maps.LatLng(51.507937, -0.076188), // default to London
mapTypeId: window.google.maps.MapTypeId.ROADMAP
};
map.setOptions(myOptions);
directionsDisplay.setMap(map);
},
fitBounds: function (map) {
var bounds = new window.google.maps.LatLngBounds();
// extend bounds for each record
jQuery.each(stops, function (key, val) {
var myLatlng = new window.google.maps.LatLng(val.Geometry.Latitude, val.Geometry.Longitude);
bounds.extend(myLatlng);
});
map.fitBounds(bounds);
},
calcRoute: function (directionsService, directionsDisplay) {
var batches = [];
var itemsPerBatch = 10; // google API max = 10 - 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: new window.google.maps.LatLng(stops[j].Geometry.Latitude, stops[j].Geometry.Longitude),
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--;
}
// now we should have a 2 dimensional array with a list of a list of waypoints
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,
travelMode: window.google.maps.TravelMode.WALKING
};
(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);
var legs = combinedResults.routes[0].legs;
// alert(legs.length);
for (var i=0; i < legs.length;i++){
var markerletter = "A".charCodeAt(0);
markerletter += i;
markerletter = String.fromCharCode(markerletter);
createMarker(directionsDisplay.getMap(),legs[i].start_location,"marker"+i,"some text for marker "+i+"<br>"+legs[i].start_address,markerletter);
}
var i=legs.length;
var markerletter = "A".charCodeAt(0);
markerletter += i;
markerletter = String.fromCharCode(markerletter);
createMarker(directionsDisplay.getMap(),legs[legs.length-1].end_location,"marker"+i,"some text for the "+i+"marker<br>"+legs[legs.length-1].end_address,markerletter);
}
}
});
})(k);
}
}
};
}
var infowindow = new google.maps.InfoWindow(
{
size: new google.maps.Size(150,50)
});
var icons = new Array();
icons["red"] = new google.maps.MarkerImage("mapIcons/marker_red.png",
// This marker is 20 pixels wide by 34 pixels tall.
new google.maps.Size(20, 34),
// The origin for this image is 0,0.
new google.maps.Point(0,0),
// The anchor for this image is at 9,34.
new google.maps.Point(9, 34));
function getMarkerImage(iconStr) {
if ((typeof(iconStr)=="undefined") || (iconStr==null)) {
iconStr = "red";
}
if (!icons[iconStr]) {
icons[iconStr] = new google.maps.MarkerImage("http://www.google.com/mapfiles/marker"+ iconStr +".png",
// This marker is 20 pixels wide by 34 pixels tall.
new google.maps.Size(20, 34),
// The origin for this image is 0,0.
new google.maps.Point(0,0),
// The anchor for this image is at 6,20.
new google.maps.Point(9, 34));
}
return icons[iconStr];
}
// Marker sizes are expressed as a Size of X,Y
// where the origin of the image (0,0) is located
// in the top left of the image.
// Origins, anchor positions and coordinates of the marker
// increase in the X direction to the right and in
// the Y direction down.
var iconImage = new google.maps.MarkerImage('mapIcons/marker_red.png',
// This marker is 20 pixels wide by 34 pixels tall.
new google.maps.Size(20, 34),
// The origin for this image is 0,0.
new google.maps.Point(0,0),
// The anchor for this image is at 9,34.
new google.maps.Point(9, 34));
var iconShadow = new google.maps.MarkerImage('http://www.google.com/mapfiles/shadow50.png',
// The shadow image is larger in the horizontal dimension
// while the position and offset are the same as for the main image.
new google.maps.Size(37, 34),
new google.maps.Point(0,0),
new google.maps.Point(9, 34));
// Shapes define the clickable region of the icon.
// The type defines an HTML <area> element 'poly' which
// traces out a polygon as a series of X,Y points. The final
// coordinate closes the poly by connecting to the first
// coordinate.
var iconShape = {
coord: [9,0,6,1,4,2,2,4,0,8,0,12,1,14,2,16,5,19,7,23,8,26,9,30,9,34,11,34,11,30,12,26,13,24,14,21,16,18,18,16,20,12,20,8,18,4,16,2,15,1,13,0],
type: 'poly'
};
function createMarker(map, latlng, label, html, color) {
// alert("createMarker("+latlng+","+label+","+html+","+color+")");
var contentString = '<b>'+label+'</b><br>'+html;
var marker = new google.maps.Marker({
position: latlng,
map: map,
shadow: iconShadow,
icon: getMarkerImage(color),
shape: iconShape,
title: label,
zIndex: Math.round(latlng.lat()*-100000)<<5
});
marker.myname = label;
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(contentString);
infowindow.open(map,marker);
});
return marker;
}
</script>
</head>
<body>
<div id="map"></div>
</body>
</html>