Cercles de dessin / Polygones avec des boutons de suppression individuels dans Google Maps API v3

Il s'agit d'aider les personnes qui ont essayé de mettre en œuvre ou ont cherché cette solution comme je l'ai fait depuis un certain temps. N'ayant pas réussi à trouver une solution existante, j'ai finalement décidé de le faire.

Comment ajouter un bouton Supprimer ou une marque X à une forme dessinée (cercle / polygone) afin que les formes individuelles puissent être supprimées à l'aide de ce bouton?

C'est le Lien Github pour voir la bibliothèque et aussi un exemple de travail complet.

En dessinant un cercle, un bouton de suppression (marque X) serait placé à 45 degrés (nord-est) sur la circonférence du cercle. Pour les polygones, car ils peuvent être imprévisibles, je place le bouton de suppression (marque X) à côté du premier sommet du polygone.

Vous pouvez supprimer des cercles / polygones individuels ou Effacer toutes les formes dessinées.

Pour appeler directement la bibliothèque dans votre fichier HTML, utilisez le script suivant,
<script src="https://gist.github.com/loyalvares/c4ba7420b1eb055b309ab48bdcd34219.js"></script>

C'est le lien JSFiddle avec le même.

 /* * Method that is called when Google Maps API is loaded. */ function initMap() { setInitialMapOptions(); map = getMapObject(mapOptions); drawingManager = getDrawingManagerObject(); google.maps.event.addListener(drawingManager, 'overlaycomplete', onOverlayComplete); initializeDeleteOverlayButtonLibrary(); } // Get Map Geo Center Denver, USA Coordinates var center = {lat: 39.810866, lng: -104.990347}; var map, drawingManager, mapOptions = {}; var listenerFiltersApplied = false; var overlays = {}; var circleOptions = { fillColor: "#e20000", fillOpacity: 0, strokeColor: "#e20000", strokeWeight: 4, strokeOpacity: 1, clickable: false, editable: true, suppressUndo: true, zIndex: 999 }; var polygonOptions = { editable: true, fillColor: "#e20000", fillOpacity: 0, strokeColor: "#e20000", strokeWeight: 4, strokeOpacity: 1, suppressUndo: true, zIndex: 999 }; function setInitialMapOptions() { mapOptions = { zoom: 4, center: center, styles: [ {"featureType":"road", elementType:"geometry", stylers: [{visibility:"off"}]}, //turns off roads geometry {"featureType":"road", elementType:"labels", stylers: [{visibility:"off"}]}, //turns off roads labels {"featureType":"poi", elementType:"labels", stylers: [{visibility:"off"}]}, //turns off points of interest lines {"featureType":"poi", elementType:"geometry", stylers: [{visibility:"off"}]}, //turns off points of interest geometry {"featureType":"transit", elementType:"labels", stylers: [{visibility:"off"}]}, //turns off transit lines labels {"featureType":"transit", elementType:"geometry", stylers: [{visibility:"off"}]}, //turns off transit lines geometry {"featureType":"administrative.land_parcel", elementType:"labels", stylers: [{visibility:"off"}]}, //turns off administrative land parcel labels {"featureType":"administrative.land_parcel", elementType:"geometry", stylers: [{visibility:"off"}]}, //turns off administrative land parcel geometry {"featureType":"water", elementType:"geometry", stylers: [{color: '#d1e1ff'}]}, //sets water color to a very light blue {"featureType":"landscape", elementType:"geometry", stylers: [{color: '#fffffa'}]}, //sets landscape color to a light white color ], mapTypeControl: false, panControl: true, panControlOptions: { position: google.maps.ControlPosition.RIGHT_CENTER }, streetViewControl: false, scaleControl: false, zoomControl: true, zoomControlOptions: { style: google.maps.ZoomControlStyle.SMALL, position: google.maps.ControlPosition.RIGHT_BOTTOM }, minZoom: 2 }; } function getMapObject(mapOptions) { var map = new google.maps.Map(document.getElementById('map'), mapOptions); return map; } function getDrawingManagerObject(drawingManagerOptions) { var drawingManager = new google.maps.drawing.DrawingManager({ drawingMode: null, drawingControl: true, drawingControlOptions: { position: google.maps.ControlPosition.TOP_CENTER, drawingModes: [ google.maps.drawing.OverlayType.CIRCLE, google.maps.drawing.OverlayType.POLYGON ] }, circleOptions: circleOptions, polygonOptions: polygonOptions }); drawingManager.setMap(map); return drawingManager; } /* -- Overlay Functions Begin Here -- */ function onOverlayComplete(shape) { addDeleteButtonToOverlay(shape); addOverlayListeners(shape); if(listenerFiltersApplied) { listenerFiltersApplied = false; } } function addOverlayListeners(shape) { // Filters already applied. if(listenerFiltersApplied) { return; } if (shape.type == google.maps.drawing.OverlayType.POLYGON) { setBoundsChangedListener(shape); } if (shape.type == google.maps.drawing.OverlayType.CIRCLE) { setCenterChangedListener(shape); setRadiusChangedListener(shape); } } function setBoundsChangedListener(shape) { // Add listeners for each path of the polygon. shape.overlay.getPaths().forEach(function(path, index){ // New point google.maps.event.addListener(path, 'insert_at', function(){ listenerFiltersApplied = true; onOverlayComplete(shape); }); // Point was removed google.maps.event.addListener(path, 'remove_at', function(){ listenerFiltersApplied = true; onOverlayComplete(shape); }); // Point was moved google.maps.event.addListener(path, 'set_at', function(){ listenerFiltersApplied = true; onOverlayComplete(shape); }); }); } function setCenterChangedListener(shape) { google.maps.event.addListener(shape.overlay, 'center_changed', function() { listenerFiltersApplied = true; onOverlayComplete(shape); }); } function setRadiusChangedListener(shape) { google.maps.event.addListener(shape.overlay, 'radius_changed', function() { listenerFiltersApplied = true; onOverlayComplete(shape); }); } function addDeleteButtonToOverlay(shape) { var deleteOverlayButton = new DeleteOverlayButton(); if(("deleteButton" in shape) && (shape.deleteButton != null)) { shape.deleteButton.div.remove(); shape.deleteButton = deleteOverlayButton; } else { shape.deleteButton = deleteOverlayButton; } if(shape.type == google.maps.drawing.OverlayType.CIRCLE) { var radiusInKms = convertDistance(Math.round(shape.overlay.getRadius()), "metres", "kms"); var circleCenter = new google.maps.LatLng(shape.overlay.getCenter().lat(), shape.overlay.getCenter().lng()); var deleteOverlayButtonPosition = circleCenter.destinationPoint(30, radiusInKms); deleteOverlayButton.open(map, deleteOverlayButtonPosition, shape); } else if (shape.type == google.maps.drawing.OverlayType.POLYGON) { deleteOverlayButton.open(map, shape.overlay.getPath().getArray()[0], shape); } if (!('uid' in shape)) { shape.uid = Math.random().toString(36).substring(2) + (new Date()).getTime().toString(36); } overlays[shape.uid] = shape; } function clearAllOverlays() { for(var shapeId in overlays) { if(overlays.hasOwnProperty(shapeId)) { var shape = overlays[shapeId]; if(("deleteButton" in shape) && (shape.deleteButton != null)) { shape.deleteButton.div.remove(); } shape.overlay.setMap(null); } } overlays = {}; } /* * Add any code that needs to be run or cleaned up in this method. * This method is called in DeleteOverlayButton.removeShape(). */ function callOnDelete(shape) { if(shape['uid'] in overlays) { delete overlays[shape['uid']]; } } /* -- Overlay Functions End Here -- */ function convertDistance(distanceValue, actualDistanceUnit, expectedDistanceUnit) { var distanceInKms = 0; switch(actualDistanceUnit) { case "miles": distanceInKms = distanceValue/0.62137; break; case "kms": distanceInKms = distanceValue; break; case "metres": distanceInKms = distanceValue/1000; break; default: distanceInKms = undefined; } switch(expectedDistanceUnit) { case "miles": return distanceInKms * 0.62137; case "kms": return distanceInKms; case "metres": return distanceInKms * 1000; default: return undefined; } } /* ***** Custom Library for Delete Overlay Button (Start) ***** */ /** * A HTML Button that lets a user delete a component. * @constructor * @author: Loy Alvares */ function DeleteOverlayButton() { this.div = document.createElement('div'); this.div.id = 'deleteOverlayButton'; this.div.className = 'deleteOverlayButton'; this.div.title = 'Delete'; this.div.innerHTML = '<span id="x">X</span>'; var button = this; google.maps.event.addDomListener(this.div, 'click', function() { button.removeShape(); button.div.remove(); }); } function initializeDeleteOverlayButtonLibrary() { /* This needs to be initialized by initMap() */ DeleteOverlayButton.prototype = new google.maps.OverlayView(); /** * Add component to map. */ DeleteOverlayButton.prototype.onAdd = function() { var deleteOverlayButton = this; var map = this.getMap(); this.getPanes().floatPane.appendChild(this.div); }; /** * Clear data. */ DeleteOverlayButton.prototype.onRemove = function() { google.maps.event.removeListener(this.divListener_); this.div.parentNode.removeChild(this.div); // Clear data this.set('position'); this.set('overlay'); }; /** * Deletes an overlay. */ DeleteOverlayButton.prototype.close = function() { this.setMap(null); }; /** * Displays the Button at the position(in degrees) on the circle's circumference. */ DeleteOverlayButton.prototype.draw = function() { var position = this.get('position'); var projection = this.getProjection(); if (!position || !projection) { return; } var point = projection.fromLatLngToDivPixel(position); this.div.style.top = point.y + 'px'; this.div.style.left = point.x + 'px'; if(this.get('overlay').type == google.maps.drawing.OverlayType.POLYGON) { this.div.style.marginTop = '-16px'; this.div.style.marginLeft = '0px'; } }; /** * Displays the Button at the position(in degrees) on the circle's circumference. */ DeleteOverlayButton.prototype.open = function(map, deleteOverlayButtonPosition, overlay) { this.set('position', deleteOverlayButtonPosition); this.set('overlay', overlay); this.setMap(map); this.draw(); }; /** * Deletes the shape it is associated with. */ DeleteOverlayButton.prototype.removeShape = function() { var position = this.get('position'); var shape = this.get('overlay'); if (shape != null) { shape.overlay.setMap(null); /* Add any cleanup code or any other events in the below method. */ callOnDelete(shape); return; } this.close(); }; Number.prototype.toRadians = function() { return this * Math.PI / 180; } Number.prototype.toDegrees = function() { return this * 180 / Math.PI; } /* Based the on the Latitude/Longitude spherical geodesy formulae & scripts at http://www.movable-type.co.uk/scripts/latlong.html (c) Chris Veness 2002-2010 */ google.maps.LatLng.prototype.destinationPoint = function(bearing, distance) { distance = distance / 6371; bearing = bearing.toRadians(); var latitude1 = this.lat().toRadians(), longitude1 = this.lng().toRadians(); var latitude2 = Math.asin(Math.sin(latitude1) * Math.cos(distance) + Math.cos(latitude1) * Math.sin(distance) * Math.cos(bearing)); var longitude2 = longitude1 + Math.atan2(Math.sin(bearing) * Math.sin(distance) * Math.cos(latitude1), Math.cos(distance) - Math.sin(latitude1) * Math.sin(latitude2)); if (isNaN(latitude2) || isNaN(longitude2)) return null; return new google.maps.LatLng(latitude2.toDegrees(), longitude2.toDegrees()); } } /* ***** Custom Library for Delete Overlay Button (End) ***** */ 
 /* Always set the map height explicitly to define the size of the div element that contains the map. */ .map { height: 100%; } /* Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; } /* CSS for the Delete Button. */ .deleteOverlayButton { background: #dee0df; color: #000; /* font-family: 'Helvetica', 'Arial', sans-serif; */ font-size: 11.4px; font-weight: bold; text-align: center; width: 14px; height: 15px; border-radius: 8px; box-shadow: 1px 0px -1px rgba(0, 0, 0, .3); position: absolute; padding: 0px 0px 0px 0px; margin-top: 7px; margin-left: 8px; border: 1px solid #999; cursor: pointer; } .deleteOverlayButton:hover { background: #eee; } #clearOverlays { font-family: var(--websiteFont); top: 10%; position: absolute; right: 1%; background: rgb(34,55,65); border-radius: 4px; color: white; border: 1px solid rgb(34,55,65); padding: 2px 6px; cursor: pointer; } 
 <div id="map" class="map"></div> <input id='clearOverlays' onclick="clearAllOverlays();" type=button value="Clear Shapes" /> <link rel="stylesheet" type="text/css" href="https://code.jquery.com/ui/1.10.4/themes/ui-lightness/jquery-ui.css" /> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyD7MXQvcn_gskiZeZGhhXekqN1zjUX9fVM&libraries=drawing&callback=initMap" async defer></script>