Google Maps API points in polygons


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.

Share this post