[October 2019] A few years ago, I’ve made this ruler to measure distances on a Google Map, I’ve noticed it’s still a searched page so I’ve made some updates to keep it working.
The file Ruler.js
contains a two functions: one is used to calculate the distance between two points on the map with their position expressed in decimal degrees, and one function that physically add the ruler to the canvas of the map. Ther ruler is composed with two standard Google markers, and a poly object is used to track the line. Two labels which show the distance are placed near the markes. The labels are placed on the map with the Labels.js
class from Marc Ridley (you can reach is old blog here).
A working demo to find distance google maps is available, and you can play with it, to download the javascript files used click on these links: ruler.js, labels.js.

Instructions to use ruler.js
- First you have to register an API Key to use Google Maps objects in your site. When you have done this you can include Google scripts to enable maps on your site.
- In your HTML file place a
div
and add an attributeid
with valuemap_canvas
. Here Google will place your map. Style it with an height to see it. - On load of the page call a function to create the map. The function is simple and just declare the object of the map, the position and the initial zoom (you can find the code in the
ruler.js
file). - Call
addruler();
function to add the ruler to the map.
Here is the intial code for ruler.js
with the addruler
method, the file now is a bit changed and has more features:
function addruler() { ruler1 = new google.maps.Marker({ position: map.getCenter() , map: map, draggable: true }); ruler2 = new google.maps.Marker({ position: map.getCenter() , map: map, draggable: true }); var ruler1label = new Label({ map: map }); var ruler2label = new Label({ map: map }); ruler1label.bindTo('position', ruler1, 'position'); ruler2label.bindTo('position', ruler2, 'position'); rulerpoly = new google.maps.Polyline({ path: [ruler1.position, ruler2.position] , strokeColor: "#FFFF00", strokeOpacity: .7, strokeWeight: 8 }); rulerpoly.setMap(map); ruler1label.set('text',"0m"); ruler2label.set('text',"0m"); google.maps.event.addListener(ruler1, 'drag', function() { rulerpoly.setPath([ruler1.getPosition(), ruler2.getPosition()]); ruler1label.set('text',distance( ruler1.getPosition().lat(), ruler1.getPosition().lng(), ruler2.getPosition().lat(), ruler2.getPosition().lng())); ruler2label.set('text',distance( ruler1.getPosition().lat(), ruler1.getPosition().lng(), ruler2.getPosition().lat(), ruler2.getPosition().lng())); }); google.maps.event.addListener(ruler2, 'drag', function() { rulerpoly.setPath([ruler1.getPosition(), ruler2.getPosition()]); ruler1label.set('text',distance( ruler1.getPosition().lat(), ruler1.getPosition().lng(), ruler2.getPosition().lat(), ruler2.getPosition().lng())); ruler2label.set('text',distance( ruler1.getPosition().lat(), ruler1.getPosition().lng(), ruler2.getPosition().lat(), ruler2.getPosition().lng())); }); }
And this is the function to calculate distance
in meters (km). To change the units modify the constant using the um
variable:
function distance(lat1,lon1,lat2,lon2) { var um = "km"; // km | ft (choose the constant) var R = 6371; if (um=="ft") { R = 20924640; // ft } 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 = R * c; if(um=="km") { if (d>1) return Math.round(d)+"km"; else if (d<=1) return Math.round(d*1000)+"m"; } if(um=="ft"){ if ((d/5280)>=1) return Math.round((d/5280))+"mi"; else if ((d/5280)<1) return Math.round(d)+"ft"; } return d; }
“var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));” can be simplified to “var c = 2 * Math.asin(Math.sqrt(a));”
True – what Bob says
Thanks a lot man! You saved my life.
Just one little thing, there is a very little bug in labels.js (I know you didn’t write it)
On line 44:
maps.google.event.removeListener(this.listeners_[i]);
this should be:
google.maps.event.removeListener(this.listeners_[i]);
Regards
Adnan
thanks. will visit again
on a learning curve.
Right. I’ve fixed it. Thank you.
Excellent. Good job: my congratulation.
It would useful also to have a couple of additional features:
1)replace the couple of labels with a single label placed in the middle of the ruler line, alway on top (I tried using zIndex with no success? any idea ?)
2)the rule line binded to the objects you’re measuring the distance. Suppose we would like to continously track the distance changes among a couple of moving markers (i.e. vehicles).
PG Ornolio
Hello,
If I attribute an icon to the rulers then the behaviour changes !
How to change the standard icons ?
Thank you.
Really good and handy code.
If i wanted to change the distance to miles would this be correct?
function distance(lat1,lon1,lat2,lon2) {
var R = 3959; // km (change this constant to get miles. 3959 for m, 6371 for km also change 1000 to 1760)
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 = R * c;
if (d>1) return Math.round(d)+”miles”;
else if (d<=1) return Math.round(d*1760)+"yards";
return d;
Regards
Pani
How do you remove a ruler?
how do you remove a ruler,,, can you helps us
Very nice, i also though want to know how to remove the ruler ?
I changed the script a bit, and now you can remove the ruler. Simply pass a ‘true’ or ‘false’ to the addruler call
var ruler1;
var ruler2;
var ruler1label;
var ruler2label;
var rulerpoly;
function addruler(remove) {
if (remove) {
google.maps.event.clearListeners(ruler1, ‘drag’);
ruler1.setMap(null);
google.maps.event.clearListeners(ruler2, ‘drag’);
ruler2.setMap(null);
ruler1label.setMap(null);
ruler2label.setMap(null);
rulerpoly.setMap(null);
}
else {
ruler1 = new google.maps.Marker({
position: map.getCenter(),
map: map,
title: ‘Starting Point’,
draggable: true
});
ruler2 = new google.maps.Marker({
position: map.getCenter(),
map: map,
title: ‘End Point’,
draggable: true
});
ruler1label = new Label({ map: map });
ruler2label = new Label({ map: map });
ruler1label.bindTo(‘position’, ruler1, ‘position’);
ruler2label.bindTo(‘position’, ruler2, ‘position’);
rulerpoly = new google.maps.Polyline({
path: [ruler1.position, ruler2.position],
strokeColor: “#FFFF00″,
strokeOpacity: .7,
strokeWeight: 7
});
rulerpoly.setMap(map);
ruler1label.set(‘text’, distance(ruler1.getPosition().lat(), ruler1.getPosition().lng(), ruler2.getPosition().lat(), ruler2.getPosition().lng()));
ruler2label.set(‘text’, distance(ruler1.getPosition().lat(), ruler1.getPosition().lng(), ruler2.getPosition().lat(), ruler2.getPosition().lng()));
google.maps.event.addListener(ruler1, ‘drag’, function() {
rulerpoly.setPath([ruler1.getPosition(), ruler2.getPosition()]);
ruler1label.set(‘text’, distance(ruler1.getPosition().lat(), ruler1.getPosition().lng(), ruler2.getPosition().lat(), ruler2.getPosition().lng()));
ruler2label.set(‘text’, distance(ruler1.getPosition().lat(), ruler1.getPosition().lng(), ruler2.getPosition().lat(), ruler2.getPosition().lng()));
});
google.maps.event.addListener(ruler2, ‘drag’, function() {
rulerpoly.setPath([ruler1.getPosition(), ruler2.getPosition()]);
ruler1label.set(‘text’, distance(ruler1.getPosition().lat(), ruler1.getPosition().lng(), ruler2.getPosition().lat(), ruler2.getPosition().lng()));
ruler2label.set(‘text’, distance(ruler1.getPosition().lat(), ruler1.getPosition().lng(), ruler2.getPosition().lat(), ruler2.getPosition().lng()));
});
}
}
function distance(lat1,lon1,lat2,lon2) {
var R = 6371; // km (change this constant to get miles)
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 = R * c;
if (d>1) return Math.round(d)+”km”;
else if (d<=1) return Math.round(d*1000)+"m";
return d;
}
Sorry, I lied. It works when i test it locally, but does not work when i put it on the live server :(
I am a dumb ass – It still had the old version in cache, so make sure you clear the cache so it gets the new js file, then it works :)
thanks a lot
Nice article
Thank you very much for the code. We have a situation here where we need to do site surveys, and to help our salespeople I have set up a Google map they can add markers to. However, I took your code one step further, and thought I’d share.
This version of the code stores the rulers in an array and outputs a line for each one where our salesmen can put in the other information we need. At the end of each line, there is a “Delete Ruler” button. It deletes the line and the ruler from the page.
// Quasi global array of all of the lines we've added.
var lines = new Array();
function addruler() {
var ruler1 = new google.maps.Marker({
position: map.getCenter() ,
map: map,
draggable: true
});
var ruler2 = new google.maps.Marker({
position: map.getCenter() ,
map: map,
draggable: true
});
var ruler1label = new Label({ map: map });
var ruler2label = new Label({ map: map });
ruler1label.bindTo('position', ruler1, 'position');
ruler2label.bindTo('position', ruler2, 'position');
var rulerpoly = new google.maps.Polyline({
path: [ruler1.position, ruler2.position] ,
strokeColor: "#0098b5",
strokeOpacity: .7,
strokeWeight: 7
});
rulerpoly.setMap(map);
ruler1label.set('text',distance( ruler1.getPosition().lat(), ruler1.getPosition().lng(), ruler2.getPosition().lat(), ruler2.getPosition().lng()));
ruler2label.set('text',distance( ruler1.getPosition().lat(), ruler1.getPosition().lng(), ruler2.getPosition().lat(), ruler2.getPosition().lng()));
google.maps.event.addListener(ruler1, 'drag', function() {
rulerpoly.setPath([ruler1.getPosition(), ruler2.getPosition()]);
ruler1label.set('text',distance( ruler1.getPosition().lat(), ruler1.getPosition().lng(), ruler2.getPosition().lat(), ruler2.getPosition().lng()));
ruler2label.set('text',distance( ruler1.getPosition().lat(), ruler1.getPosition().lng(), ruler2.getPosition().lat(), ruler2.getPosition().lng()));
});
google.maps.event.addListener(ruler2, 'drag', function() {
rulerpoly.setPath([ruler1.getPosition(), ruler2.getPosition()]);
ruler1label.set('text',distance( ruler1.getPosition().lat(), ruler1.getPosition().lng(), ruler2.getPosition().lat(), ruler2.getPosition().lng()));
ruler2label.set('text',distance( ruler1.getPosition().lat(), ruler1.getPosition().lng(), ruler2.getPosition().lat(), ruler2.getPosition().lng()));
});
// Add our new ruler to an array for later reference
lines.push([ruler1, ruler2, ruler1label, ruler2label, rulerpoly]);
addLine(lines.length - 1);
}
function addLine (num) {
// This function adds a line to our page.
var div = document.getElementById('latlon');
var oldHTML = document.getElementById('latlon').innerHTML;
div.innerHTML = oldHTML + "YesNo Delete Ruler";
// Set up the event handler for the remove ruler button
document.getElementById('delruler' + num).onclick = function() {removeLine(num); return false;}
}
function removeLine (num) {
// Removes the line from our HTML page
var div = document.getElementById('ruler' + num);
div.parentNode.removeChild(div);
removeRuler(lines[num]);
}
function removeRuler (r) {
// Now we remove the ruler.
// I've unpacked the variables for readability.
var ruler1=r[0]; var ruler2=r[1]; var ruler1label=r[2]; var ruler2label=r[3]; var rulerpoly=r[4];
google.maps.event.clearListeners(ruler1, 'drag');
ruler1.setMap(null);
google.maps.event.clearListeners(ruler2, 'drag');
ruler2.setMap(null);
ruler1label.setMap(null);
ruler2label.setMap(null);
rulerpoly.setMap(null);
}
function distance(lat1,lon1,lat2,lon2) {
var R = 3959; // Here's the right settings for miles and feet
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 = R * c;
if (d>1) return Math.round(d)+"mi";
else if (d<=1) return Math.round(d*5280)+"ft";
return d;
}
Again, thanks for publishing this. You saved me a day or two.
Ooops, this ate my HTML…
div.innerHTML = oldHTML + "YesNo Delete Ruler";
Long story short, there was a bunch of HTML in here. Longer story shorter, you don’t need it.
The trick that makes this work however, is that the button id is
id='delruler" + num + "'
So that the event handler attaches the button to the right ruler.
document.getElementById('delruler' + num).onclick = function() {removeLine(num); return false;}
Notice that in the getElementById, we name each div delruler1, delruler2, etc…
Nice code, thanks for sharing! As noted in the comments above, it would be nice to have the option to delete a ruler. My solution to this was that a double-click on one of the markers deletes the ruler. Simply add
google.maps.event.addListener(ruler1, 'dblclick', function() {
ruler1.setMap(null);
ruler2.setMap(null);
ruler1label.setMap(null);
ruler2label.setMap(null);
rulerpoly.setMap(null);
});
google.maps.event.addListener(ruler2, 'dblclick', function() {
ruler1.setMap(null);
ruler2.setMap(null);
ruler1label.setMap(null);
ruler2label.setMap(null);
rulerpoly.setMap(null);
});
to the end of the addruler function.