2

Ok, here is my problem, I'll put a picture to illustrate it easier. Google maps editable hexagon

I need the user to draw some polygons, representing the coverage area.

The polygon needs to have fixed number of points (vertex) because it goes into a processing algorithm later, and it would be really slow if a polygon can contain a lot of points.

Anyway, in my example lets stick to hexagons (6 points).
The user need to be able to drag the polygon around and modify it, but not change the number of points.

I tried setting the editable: true option for the polygon, it works fine, but it gives me the situation shown on the picture. It creates a handle for every point, and another handle (semi-transparent) in the middle between each points. Now, if the user moves that semi-transparent point, it will add another point (vertex) to the polygon, and add additional two handles in the middle of newly created lines. That gives us a 7 point polygon.

The best option would be to remove those semi-transparent handles, so the user can only drag polygon points, and it that way he can't affect the total number of points.

Can I achieve this using google maps editable option?

2
  • Can you provide a jsfiddle example? Commented Apr 4, 2012 at 16:35
  • I cant provide you a working example because I'm using an external library for gmaps (not really important, it is just a wrapper, gmap3.net). Here is the code jsfiddle.net/m4uvN/1. Before you ask why setTimeout() :), the callback doesn't return in the right moment. It returns the polygon before it is rendered in the map, so I have to delay the deleting a little Commented Apr 4, 2012 at 18:25

5 Answers 5

9

Another way to achieve what you want is to forego the built-in edit-ability of the polygon and implement it yourself. This is more powerful and flexible.

First, don't make the polygon editable. Next, make a Marker for each corner of the polygon. Finally, make each marker draggable and an event listener on it's "drag" event to update the polygon.

Making the markers and adding the event listener:

for (var i=0; i<coordinates.length; i++){
    marker_options.position = coordinates[i];
    var point = new google.maps.Marker(marker_options);

    google.maps.event.addListener(point, "drag", update_polygon_closure(polygon, i));
}

Where update_polygon_closure is defined as:

function update_polygon_closure(polygon, i){
    return function(event){
       polygon.getPath().setAt(i, event.latLng); 
    }
}

Full code is in a jsfiddle here: https://jsfiddle.net/3L140cg3/16/

Sign up to request clarification or add additional context in comments.

1 Comment

how can i do this in android ? Please see My question stackoverflow.com/questions/61527262/…
5

Since no one seems to have a better solution, I'm marking my workaround as accepted, in case someone stumbles upon the same problem. Not very pretty, but gets the job done

The only solution I found so far is to hide the handles manually after the polygon has been drawn. The problem here is that the handles don't have any CSS class or id, so I have to hide all divs with opacity 0.5 (opacity of the handles). It works, but it is pretty risky, considering that something else might have the same opacity and doesn't need to be hidden.

// variables
var map, path, color;

polygon = new google.maps.Polygon({
    paths: path,
    strokeColor: color,
    strokeOpacity: 0.8,
    strokeWeight: 2,
    fillColor: color,
    fillOpacity: 0.10,
    editable: true,
});
polygon.setMap(map);
setTimeout(function(){ map.find('div[style*=opacity: 0.5]').hide(); }, 50);

3 Comments

Just hiding the 0.5 opacity divs has strange consequences though. You can still click where the divs should be and drag if the polygon is editable. This spawns another midpoint when you do it as well. Hiding the parent div avoids this side-effect. $('#multi_markers div[style*="opacity: 0.5"]').parent('div[style*="cursor: pointer"]').hide(); It's still a pretty risky solution, but it's the only one I've found as well, so thanks.
Noted, thanks. At the time of writing it did't have this issue
This is the only answer I've found so far. Indeed not perfect, but better than nothing ! But why don't you do this directly in CSS ?
2

As a slight improvement to @zolakt's answer, you can both hide the midpoint divs on the polygon and add a mousedown event listener to track when a midpoint is clicked to prevent dragging and changing the polygon:

// hide the midpoints (note that users can still click where the hidden midpoint
// divs are and drag to edit the polygon
$('#multi_markers div[style*="opacity: 0.5"]').hide();

// get the paths for the current polygon
var octopusPaths = HotelLib.octopusPolygon.getPaths();


// track when a polygon midpoint is clicked on
google.maps.event.addListener(HotelLib.octopusPolygon, 'mousedown', function(mdEvent) {


    // if a midpoint is clicked on, mdEvent.edge will have an integer value
    if(mdEvent.edge || (mdEvent.edge == 0)){

        // immediately reset the polygon to its former paths
        // effectively disabling the drag to edit functionality
        HotelLib.octopusPolygon.setPaths(octopusPaths);

        // hide the midpoints again since re-setting the polygon paths
        // will show the midpoints
        $('#multi_markers div[style*="opacity: 0.5"]').hide();

    }

});

Comments

1

I just created an alternative solution to this without having to fiddle around with setTimeout or the polyline creation. This is also a somewhat global solution, so you can basically drop it in any established program that uses Google Maps.

We'll use MutationObserver to observe when those midpoint nodes appear on the DOM and then instantly hide them. They should start appearing when something is set as editable.

Basically just put this anywhere after the map is initialized:

var editMidpointNodeObserver = new MutationObserver(function(list, observer)
{
    if($('#mapwrapper div[style*="opacity: 0.5"]').parent('div[style*="cursor: pointer"]').length > 0)
    {
        $('#mapwrapper div[style*="opacity: 0.5"]').parent('div[style*="cursor: pointer"]').remove();
    }
    
});

editMidpointNodeObserver.observe($('#mapwrapper')[0], { childList: true, subtree: true });

Change the #mapwrapper to whatever the id of your Google Maps wrapper element is. I am using jQuery here, so therefore the $('#mapwrapper')[0] to convert jQuery object to a native DOM object. Should work without jQuery as well, I am assuming you know how to convert this to vanilla js.

We also just straight up remove the nodes, so no need to worry about user being able to click invisible ones by accident or otherwise.

MutationObserver should be supported in all browsers: https://caniuse.com/mutationobserver

Comments

0

I would recommend also to delete any extra vertex, in case the user manage to create it from hiden point:

google.maps.event.addListener(
  localityPolygon.getPath(),
  "insert_at",
  (function (key) {
    return function (index) {
      if (localityPolygon.getPath().getLength() > 6) {
        localityPolygon.getPath().removeAt(index);

        setTimeout(function () {
          var map = document.getElementById("dialogMap");
          var divs = map.querySelectorAll("div[style*='opacity: 0.5']");
          divs.forEach(function (div) {
            div.style.display = "none";
          });
        }, 50);
      }
    };
  })(key)
);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.