8

The Google Maps API for a Polygon does not offer a drag method.

What would be an efficient way of implementing such a feature (i.e., sufficiently optimised so that it would not kill a four year old laptop)?

Thank you!

Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315

5 Answers5

6

I found the Google Maps V2 Polygon Implementation to be very limiting for the needs I have had and solved it by creating a custom overlay. My group is currently stuck on IE6 so I have yet to migrate over to Google Maps V3 - but taking a quick look at the API shows that you could probably do a similar thing that I did in V2 with V3.

Essentially the idea is:

  1. Create a Custom Overlay
  2. Populate it with your own SVG/VML Polygons and attach a drag event to this custom polygon object

Custom Overlays:

Here is some information to get you started on making your own custom overlay:

http://code.google.com/apis/maps/documentation/javascript/overlays.html#CustomOverlays


Creating your own "Dragable" Polygon Object:

Once you get that down you'll want to add your own polygons to the custom overlay instead of using GPolygons. I went through the painful process of learning SVG/VML and writing a library to bridge SVG/VML together - you could do that, but I would recommend starting by trying to use another library such as Raphaël.

http://raphaeljs.com/

Using Raphaël will save you a whole lot of time trying to figure out how to get cross-browser Vector Graphic (Polygon) functionality - and best of all it supports drag events already, here is an example from their library:

http://raphaeljs.com/graffle.html

Once you have a custom overlay and you are able to throw some Raphaël objects onto it the last step is to translate the coordinates you want from a Lat/Lng value to a Pixel value. This is available in the MapCanvasProjection of V3:

http://code.google.com/apis/maps/documentation/javascript/reference.html#MapCanvasProjection

You can use fromLatLngToDivPixel to figure out what the actual pixel values are for the points on your Raphael polygon, draw it, then add it to the overlay with a drag event.

John
  • 1,037
  • 11
  • 19
  • As an afterthought - I found that my Polygon objects performed much faster than GPolygon objects in V2 - I do not know how they perform in V3, but I found that creating your own Polygons directly with SVG/VML results in a much lighter object that you have more control over directly. I am not sure if Google is using SVG/VML or Canvas now to draw their Polygons - it has changed a couple times throughout versions. – John Sep 02 '10 at 15:57
4

Since version 3.11 (dated Jan 22, 2013) it's possible to just set the draggable property onto the google.maps.Polygon instance; see this example.

If you want to programmatically move a polygon, you'll need a custom Google Maps Extension which I wrote, as the API does not provide such a method.

Bramus
  • 1,732
  • 14
  • 19
3

Here's how I do it. Find the approximate center of the polygon, and add a marker, then add a drag listener to the marker. On lat/lng change, subtract the difference from the original marker lat/lng, subtract the difference to each of the paths, then, set the original position to the new position. Make sure that in your javascript api call that you have library=geometry,drawing

google.maps.event.addListener(draw, 'overlaycomplete', function(shape) {
// POLYGON
      if (shape.type == 'polygon') {
        var bounds = new google.maps.LatLngBounds(); var i;  
        var path = shape.overlay.getPath();
        for (i = 0; i < path.length; i++) { bounds.extend(path.getAt(i)); }
        shape.latLng = bounds.getCenter();
        marker = getMarker(map,shape);
        shape.overlay.marker = marker;
        markers.push(marker); 
      }
      google.maps.event.addListener(marker, 'drag', function(event) {
         shape.overlay.move(event.latLng, shape, path);
      });

          google.maps.event.addListener(shape.overlay, 'rightclick', function() {
            this.setMap(null);
            this.marker.setMap(null);
            draw.setDrawingMode('polygon');
          });

  });
}
google.maps.Polygon.prototype.move = function(latLng, shape, p) {
    var lat = latLng.lat();
    var lng = latLng.lng();

    latDiff = shape.latLng.lat()-lat;
    lngDiff = shape.latLng.lng()-lng;

   for (i = 0; i < p.length; i++) {
    pLat = p.getAt(i).lat();
    pLng = p.getAt(i).lng();
    p.setAt(i,new google.maps.LatLng(pLat-latDiff,pLng-lngDiff));
   }
   shape.latLng = latLng; 
}
function getMarker(map,shape){
  var infowindow = new google.maps.InfoWindow();
  if(shape.type=='polygon'){ latLng = shape.latLng; }
  marker = new google.maps.Marker({
              position: latLng,
              map:map,
              draggable:true,
              clickable: true,
              animation: google.maps.Animation.DROP
            });
           shape.overlay.marker = marker;
           shape.overlay.bindTo('center',marker,'position');
           google.maps.event.addListener(marker, 'click', (function(marker) {
            return function() {
              infowindow.setContent('polygon');
              infowindow.open(map, marker);
              toggleBounce(marker);
            }
          })(marker));
          google.maps.event.addListener(infowindow,'closeclick', (function(marker) {      
            return function() {
            marker.setAnimation(null);
            }
          })(marker));
 return marker;
}

If you have any questions, feel free to contact me.

Eric Leroy
  • 1,830
  • 1
  • 18
  • 31
1

You could have markers for each point on the polygon, these markers could have drag and at the end of each drag, the polygon could be redrawn.

You could also have a marker in the center of the polygon representing the polygon as a whole, when you move that marker, every marker could be moved by the same amount to maintain the shape.

Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
paullb
  • 4,293
  • 6
  • 37
  • 65
  • Can you add click/drag listeners etc on the GMap itself and do the calculation if the click was inside the polyon or not? – paullb Jun 25 '10 at 10:12
1

Okay - so after seeing the website you are trying to implement I started to feel like Raphael may not be necessary because it is a pretty heavy JS Library - and if you are only trying to draw a rectangle polygon I thought, why not just do it with a single lightweight DIV instead?

However I think the Raphael solution would still hold water for many other cases - so I think I'll just post another possible answer.

Here is a working example I threw together:

http://www.johnmick.net/drag-div-v3/

Feel free to take a look at the source:

http://www.johnmick.net/drag-div-v3/js/main.js

Essentially we do the following

  1. Create the Custom Overlay
  2. Create the draggable div polygon and, using jQuery UI, make it draggable
  3. Tie an event that listens to when the dragging has stopped that updates the LatLng position of the rectangle
  4. Add the object to the Custom Overlay
  5. Implement the draw function to redraw the rectangle during zooms and pans

Currently I am only storing one LatLng value for the Rectangle (being the top left corner) - you could easily extend this example to store all 4 points of the rectangle and have the shape dynamically resize itself on zooms. You may want to do that, otherwise as users zoom out they will get a climate report for a larger and larger area.

John
  • 1,037
  • 11
  • 19
  • @Dave: Ah now that sounds like a interesting thing to solve - for doing complex polygon shapes you may be best off learning SVG/VML - it's really not so bad, if not a little tedious - that way you could keep track of however many points and draw whatever shape in any size you want. Oh and you are very welcome! Making my own improved GPolygon objects was a huge dilemma for me a couple years ago so I am really happy to pass on anything I learned to help somebody else out. – John Sep 02 '10 at 19:27
  • As of today 12/4/2015, I tried both on IE and Chrome, the polygon is not draggable. – user2618844 Dec 04 '15 at 14:43