Duncan's blog

August 9, 2014

Google Maps API – draggable polygons

Filed under: Google Maps,Javascript — duncan @ 8:00 am
Tags: , , , ,

This post was in response to a question on StackOverflow, asking how to make a draggable and editable polygon, but restrict it to always be a pentagon.

Firstly, it’s quite simple to allow a user to drag the points on a polygon, just add ‘editable: true’ to the PolygonOptions.  However it’s not possible to restrict the number of points it has; by default Google places draggable spots on the mid-points of each edge; if the user drags these then it adds an additional point.

<script>
    function initialize() {
        var map = new google.maps.Map(document.getElementById("map_canvas"), {
            zoom: 15,
            center: {lat: 51.476706, lng: 0},
            mapTypeId: google.maps.MapTypeId.ROADMAP
        });
        
        // create an array of coordinates for a pentagonal polygon
        var arrCoords = [
            new google.maps.LatLng(51.474821, -0.001935),
            new google.maps.LatLng(51.474647, 0.003966),
            new google.maps.LatLng(51.477708, 0.004073),
            new google.maps.LatLng(51.479753, 0.000468),
            new google.maps.LatLng(51.477654, -0.002192)
        ];
        
        var polygon = new google.maps.Polygon({
            editable: true,
            paths: arrCoords,
            strokeColor: "#FF0000",
            strokeOpacity: 0.8,
            strokeWeight: 2,
            fillColor: "#FF0000",
            fillOpacity: 0.35,
            map: map
        });
    }
    
    google.maps.event.addDomListener(window, 'load', initialize);
</script>

This then gives us a pentagon like:

polygon1

If we start to drag any of those points in the centre of an edge, we can ultimately end up with something like this, very far from being a pentagon:

polygon2

So, how to get around this?  My idea is that instead of making the polygon editable, we simply add our own markers to each of the 5 vertices.  These are draggable, and in response to the user dragging them, we update the polygon path.

<script>
    function initialize() {
        var map = new google.maps.Map(document.getElementById("map_canvas"), {
            zoom: 15,
            center: {lat: 51.476706, lng: 0},
            mapTypeId: google.maps.MapTypeId.ROADMAP
        });
        
        // create an array of coordinates for a pentagonal polygon
        var arrCoords = [
            new google.maps.LatLng(51.474821, -0.001935),
            new google.maps.LatLng(51.474647, 0.003966),
            new google.maps.LatLng(51.477708, 0.004073),
            new google.maps.LatLng(51.479753, 0.000468),
            new google.maps.LatLng(51.477654, -0.002192)
        ];
        
        var polygon = new google.maps.Polygon({
            paths: arrCoords,
            strokeColor: "#FF0000",
            strokeOpacity: 0.8,
            strokeWeight: 2,
            fillColor: "#FF0000",
            fillOpacity: 0.35,
            map: map
        });
        
        // add a marker at each coordinate
        for (var i = 0; i < arrCoords.length; i++) {
             var marker = new google.maps.Marker({
                position: arrCoords[i],
                map: map,
                draggable: true
            });
                        
            bindMarker(marker, arrCoords, i, polygon);
        }
    }
    
    function bindMarker(marker, arrCoords, i, polygon) {
        google.maps.event.addListener(marker, 'dragend', function(e) {
             arrCoords[i] = e.latLng;
             polygon.setPath(arrCoords);
        });
    }
    
    google.maps.event.addDomListener(window, 'load', initialize);
</script>

When a marker gets dragged, we update the coordinates of the corresponding point in our path array.  We then update the path on the polygon.  Simple!

polygon3

After dragging the markers, we still have only 5 edges:

polygon4

So that’s all very well and good, but those markers can be a bit obtrusive looking on our page.  Well we can specify custom icons instead.  In this case we can even just use the Symbol class to come up with something much nicer looking:

            var marker = new google.maps.Marker({
                position: arrCoords[i],
                map: map,
                draggable: true,
                icon: {
                    path: google.maps.SymbolPath.CIRCLE,
                    scale: 5,
                    strokeColor: "#FF0000",
                    strokeOpacity: 0.8,
                    strokeWeight: 2,
                    fillColor: "#FFFFFF",
                    fillOpacity: 1
                }
            });

polygon5

Also it might be that we want to be able to drag the entire polygon to another location on the map (and was a requirement of the original question on StackOverflow).  In that case we have to update the markers at the same time.  It gets slightly more complicated here; basically I create an array of markers (always useful anyway).  Each time we drag the polygon, we update this array.  We also remove any markers previously created.

<script>
    function initialize() {
        var map = new google.maps.Map(document.getElementById("map_canvas"), {
            zoom: 15,
            center: {lat: 51.476706, lng: 0},
            mapTypeId: google.maps.MapTypeId.ROADMAP
        });
        
        // create an array of coordinates for a pentagonal polygon
        var arrCoords = [
            new google.maps.LatLng(51.474821, -0.001935),
            new google.maps.LatLng(51.474647, 0.003966),
            new google.maps.LatLng(51.477708, 0.004073),
            new google.maps.LatLng(51.479753, 0.000468),
            new google.maps.LatLng(51.477654, -0.002192)
        ];
        
        var polygon = new google.maps.Polygon({
            paths: arrCoords,
            strokeColor: "#FF0000",
            strokeOpacity: 0.8,
            strokeWeight: 2,
            fillColor: "#FF0000",
            fillOpacity: 0.35,
            map: map,
            draggable: true
        });
        
        var markers = addMarkers(arrCoords, map, polygon, []);
        
        google.maps.event.addListener(polygon, 'dragend', function(e) {
             markers = addMarkers(this.getPath().getArray(), map, this, markers);
        });
    }
    
    function addMarkers(arrCoords, map, polygon, oldMarkers) {
        var markers = [];
        
        // clear any existing markers
        for (var i = 0; i < oldMarkers.length; i++) {
            oldMarkers[i].setMap(null);
        }
        
        // add a marker at each coordinate
        for (var i = 0; i < arrCoords.length; i++) {
             var marker = new google.maps.Marker({
                position: arrCoords[i],
                map: map,
                draggable: true,
                icon: {
                    path: google.maps.SymbolPath.CIRCLE,
                    scale: 5,
                    strokeColor: "#FF0000",
                    strokeOpacity: 0.8,
                    strokeWeight: 2,
                    fillColor: "#FFFFFF",
                    fillOpacity: 1
                }
            });
        
            bindMarker(marker, arrCoords, i, polygon);
            
            markers.push(marker);
        }
        
        return markers;
    }
    
    function bindMarker(marker, arrCoords, i, polygon) {
        google.maps.event.addListener(marker, 'dragend', function(e) {
             arrCoords[i] = e.latLng;
             polygon.setPath(arrCoords);
        });
    }
    
    google.maps.event.addDomListener(window, 'load', initialize);
</script>

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: