Google Map Polygon – Polyline Editable

User Editable Google Maps Polygon

The correct way of implementing a user editable google maps polygon when you want your users to enclose an area in a map 
Assuming you have already a working map on your project, an API key and you have setup your InitMap() function.
Now for this case we will need to create two buttons on the map, i guess one to Undo a point and one to delete everything and start all over again, in order to make those two buttons to look like the default google buttons im gonna create a function that will take care of the css styles.

    function BuildGoogleButton(text, icon) {
        var controlMarkerUI = document.createElement('button');
        controlMarkerUI.style.cursor = 'pointer';
        controlMarkerUI.style.backgroundColor = '#ffffff';
        controlMarkerUI.style.height = '38px';
        controlMarkerUI.style.width = '40px';
        controlMarkerUI.innerHTML = icon;
        controlMarkerUI.style.marginLeft = '10px';
        controlMarkerUI.style.marginTop = '10px';
        controlMarkerUI.title = text;
        controlMarkerUI.style.border = '0px none';
        controlMarkerUI.style.margin = '10px';
        controlMarkerUI.style.padding = '0px';
        controlMarkerUI.style.boxShadow = ' rgba(0, 0, 0, 0.3) 0px 1px 4px -1px';
        controlMarkerUI.classList.add("gm-control-active");
        return controlMarkerUI;

    }

.


For the buttons i am using material icons so in case you have to use your own or just <link href=”https://fonts.googleapis.com/icon?family=Material+Icons” rel=”stylesheet”>
 
Now back to our IniMap() i will prefer to use cursor crosshair , which is not a hair cut as it sounds but a default cursor that looks like targeting so the user will have the feeling he is about to target, so my initMap() looks like this.

    function initMap() {

        /*global google*/ // To disable any eslint 'google not defined' errors
        var map = new google.maps.Map(document.getElementById('map'), {
            center: new google.maps.LatLng(37.983810, 23.727539),
            zoom: 15,
            scaleControl: true,
            clickableIcons: false,
            streetViewControl: false,
            mapTypeControl: false,
            draggableCursor: 'crosshair',

        });
Thanks to stackoverflow i found a piece of snippet that was pretty much self explanatory of how to add an event listener click on the whole map and then for every click we get the coordinates with clickEvent.latlng place a marker on that specific point where the user clicked, then we also add an event listener for the very first marker it self, so the next time the first ever marker will be clicked we will copy the path from the polyline and we will pass it to a new polygon after we destroy the polyline.
How this code can get better, i will let some little challenges for you.
A. Remove event listener of the first pin when it is removed via undo, or reset (for memory leaks). 
B. Check if there are more than 2 points when you create a polygon and set isclosed to false, (so.. if the first pin clicked twice the map will still work) 

C. Maybe you will need to move the for loop where we build our bounds array to a seperate function that will get the poly though args, and then call that function  for everytime a polygon closes and from the marker click event listener.

The whole initMap() function:

    function initMap() {

        /*global google*/ // To disable any eslint 'google not defined' errors
        var map = new google.maps.Map(document.getElementById('map'), {
            center: new google.maps.LatLng(37.983810, 23.727539),
            zoom: 15,
            scaleControl: true,
            clickableIcons: false,
            streetViewControl: false,
            mapTypeControl: false,
            draggableCursor: 'crosshair',

        });
        let strokeColor = '#1F00FF';
        var isClosed = false;
        var marker = [];
        var poly = new google.maps.Polyline({ map: map, path: [], strokeColor: strokeColor, strokeOpacity: 1.0, strokeWeight: 2 });

        // Create a div to hold the control.
        var controlDiv = document.createElement('div');
        var controlMarkerUI = BuildGoogleButton('Undo', '<i style="color: #404040" class="material-icons">undo</i>');
        controlMarkerUI.addEventListener('click', function () {
            if (poly.getPath().length > 0) {
                marker[poly.getPath().length - 1].setMap(null);
                poly.getPath().pop();

                isClosed = false;
                let path = poly.getPath();
                poly.setMap(null);

                poly = new google.maps.Polyline({ map: map, path, strokeColor, strokeOpacity: 1.0, strokeWeight: 2 });
            }
        });
        controlDiv.appendChild(controlMarkerUI);
        controlMarkerUI = BuildGoogleButton('Reset', '<i style="color: #404040" class="material-icons">delete</i>');
        controlDiv.appendChild(controlMarkerUI);
        controlMarkerUI.addEventListener('click', function () {
            while (poly.getPath().length > 0) {
                marker[poly.getPath().length - 1].setMap(null);
                poly.getPath().pop();


            }
            poly.setMap(null);
            poly = new google.maps.Polyline({ map: map, path: [], strokeColor, strokeOpacity: 1.0, strokeWeight: 2 });
            isClosed = false;
        });
        map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(controlDiv);
        google.maps.event.addListener(map, 'click', function (clickEvent) {

            if (isClosed)
                return;
            var markerIndex = poly.getPath().length;
            var isFirstMarker = markerIndex === 0;
            let iconmap = '';
            if (poly.getPath().length === 0) {
                iconmap = 'http://maps.google.com/mapfiles/ms/icons/purple-dot.png';
            } else {
                iconmap = 'http://maps.google.com/mapfiles/ms/icons/orange-dot.png';
            }

            marker[markerIndex] = new google.maps.Marker({ map: map, position: clickEvent.latLng, draggable: true, icon: iconmap });

            if (isFirstMarker) {
                //Your challenge #1
                google.maps.event.addListener(marker[markerIndex], 'click', function () {
                    if (isClosed) return; //your Challenge #2 
                    var path = poly.getPath();
                    poly.setMap(null);
                    poly = new google.maps.Polygon({ map: map, path: path, strokeColor: strokeColor, strokeOpacity: 0.8, strokeWeight: 2, fillColor: "#007DFF", fillOpacity: 0.35 });
                    isClosed = true;
                    //that means we can get some lat lngs of the shape
                    var bounds = [];
                    for (var i = 0; i < poly.getPath().length; i++) {
                        var point = {
                            lat: poly.getPath().getAt(i).lat(),
                            lng: poly.getPath().getAt(i).lng()
                        };
                        bounds.push(point);
                    }
                    //Console log our array with the points.
                    console.log(bounds);
                });
            }
            //change coordinates of point when a marker is dragged
            google.maps.event.addListener(marker[markerIndex], 'drag', function (dragEvent) {
                poly.getPath().setAt(markerIndex, dragEvent.latLng);
            });
            poly.getPath().push(clickEvent.latLng);
        });
    }

Leave a Reply