Update 27th March 2012: The Google Maps Geometry library now contains a containsLocation method which will achieve the same thing as detailed below. I’d recommend you use that instead.
Discovering whether a point on a map is inside a polygon can be very useful, especially now that modern browsers and phones are location aware. If you can tell if your user is within a specific boundary then you can present them with immediately relevant information. An example I have used is telling the user they are in the coverage area of a local business (Gascall).
Assuming you’ve already set up your map, the Google maps API allows you to create polygons using simple code.
var path = [
new google.maps.LatLng(51.73383267274113, -1.03271484375),
new google.maps.LatLng(51.45400691005984, -1.4501953125),
new google.maps.LatLng(51.83577752045251, -0.8843994140625)];
var polygon = new google.maps.Polygon({path:path, clickable:false});
polygon.setMap(map);
You can create a Google maps point from latitude/longitude values or the users location:
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var point = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
});
}
Determining if the point is inside the polygon
The google maps api does not already provide a method for checking points in polygons. Some research has already been done on this, but I couldn’t find a complete solution in Google maps api v3 that also catered for crossing 180 degrees longitude. I would have done a straight translation into Google maps api v3, but decided to actually try to understand what was going on.
After researching a bit I stumbled across the Ray-casting algorithm which will determine if an X-Y coordinate is inside a plotted shape. This will translate to latitude and longitude. The following extends the google.maps.polygon.prototype to use this algorithm. Simply include this code at a point in the code after google.maps has loaded:
google.maps.Polygon.prototype.Contains = function(point) {
// ray casting alogrithm http://rosettacode.org/wiki/Ray-casting_algorithm
var crossings = 0,
path = this.getPath();
// for each edge
for (var i=0; i < path.getLength(); i++) {
var a = path.getAt(i),
j = i + 1;
if (j >= path.getLength()) {
j = 0;
}
var b = path.getAt(j);
if (rayCrossesSegment(point, a, b)) {
crossings++;
}
}
// odd number of crossings?
return (crossings % 2 == 1);
function rayCrossesSegment(point, a, b) {
var px = point.lng(),
py = point.lat(),
ax = a.lng(),
ay = a.lat(),
bx = b.lng(),
by = b.lat();
if (ay > by) {
ax = b.lng();
ay = b.lat();
bx = a.lng();
by = a.lat();
}
// alter longitude to cater for 180 degree crossings
if (px < 0) { px += 360 };
if (ax < 0) { ax += 360 };
if (bx < 0) { bx += 360 };
if (py == ay || py == by) py += 0.00000001;
if ((py > by || py < ay) || (px > Math.max(ax, bx))) return false;
if (px < Math.min(ax, bx)) return true;
var red = (ax != bx) ? ((by - ay) / (bx - ax)) : Infinity;
var blue = (ax != px) ? ((py - ay) / (px - ax)) : Infinity;
return (blue >= red);
}
};
Then to check a point:
var point = new google.maps.LatLng(52.05249047600099, -0.6097412109375);
var polygon = new google.maps.Polygon({path:[INSERT_PATH_ARRAY_HERE]});
if (polygon.Contains(point)) {
// point is inside polygon
}
I hope this proves useful. Let me know if you have any suggestions/comments. Test page.