How to know radius while drawing a circle on Google Maps

后端 未结 1 702
囚心锁ツ
囚心锁ツ 2021-02-13 16:36

What I have :

As per Google suggestion we have used MVC object and the events DistanceWidget & RadiusWidget to display the

1条回答
  •  长发绾君心
    2021-02-13 17:20

    a nice challenge indeed. As @DaveAlperovich has commented, you can't use the DrawingManager to retrieve this piece of information; While drawing, you don't have any access to the circle; You have to wait for the DrawingManager to trigger the circlecomplete event to get a reference to this circle.

    Nevertheless, if you can't have a real manager, just fake it. See the snippet and the description right below.

    var FakeDrawer = function(controlDiv, map) {
      var self = this;
    
      /* Initialization, some styling ... */
      self._map = map;
      self.initControls(controlDiv);
    
      /* Setup the click event listener: drawingmode for the circle control */
      google.maps.event.addDomListener(self._controls.circle, 'click', function() {
        /* Ensure consistency */
        self.reset();
    
        /* Bind the drawing mode */
        self._map.setOptions({
          draggableCursor: "crosshair"
        });
        self._drawListener = self._map.addListener('mousedown', self.drawingMode(self));
      });
    
      /* Just reset things for the stop controls */
      google.maps.event.addDomListener(self._controls.stop, 'click', function() {
        self.reset();
      });
    };
    
    FakeDrawer.prototype.drawingMode = function(self) {
      return function(center) {
        /* Let's freeze the map during drawing */
        self._map.setOptions({
          draggable: false
        });
    
        /* Create a new circle which will be manually scaled */
        var circle = new google.maps.Circle({
          fillColor: '#000',
          fillOpacity: 0.3,
          strokeWeight: 2,
          clickable: false,
          editable: false,
          map: self._map,
          radius: 1,
          center: center.latLng,
          zIndex: 1
        });
    
        /* Update the radius on each mouse move */
        var onMouseMove = self._map.addListener('mousemove', function(border) {
          var radius = 1000 * self.distanceBetweenPoints(center.latLng, border.latLng);
          circle.setRadius(radius);
    
          /* Here is the feature, know the radius while drawing */
          google.maps.event.trigger(self, 'drawing_radius_changed', circle);
        });
    
        /* The user has finished its drawing */
        google.maps.event.addListenerOnce(self._map, 'mouseup', function() {
          /* Remove all listeners as they are no more required */
          google.maps.event.removeListener(onMouseMove);
    
          circle.setEditable(true);
    
          /* Restore some options to keep a consistent behavior */
          self.reset();
    
          /* Notify listener with the final circle */
          google.maps.event.trigger(self, 'circlecomplete', circle);
        });
      };
    };
    
    FakeDrawer.prototype.reset = function() {
      var self = this;
    
      self._map.setOptions({
        draggableCursor: "",
        draggable: "true"
      });
    
      /* Remove any applied listener */
      if (self._drawListener) {
        google.maps.event.removeListener(self._drawListener);
      }
    };
    
    /* Create views and associated css */
    FakeDrawer.prototype.initControls = function(controlDiv) {
      var self = this;
    
      function createControlUI(title, image) {
        var controlUI = document.createElement('div');
        controlUI.style.backgroundColor = '#fff';
        controlUI.style.border = '1px solid rgba(0, 0, 0, .15)';
        controlUI.style.boxShadow = '1 4px -1px rgba(0, 0, 0, .3)';
        controlUI.style.marginTop = '10px';
        controlUI.style.textAlign = 'center';
        controlUI.style.width = '25px';
        controlUI.style.height = '25px';
        controlUI.style.display = 'inline-block';
        controlUI.title = title;
        if (image == "circle") {
          controlUI.style.borderLeft = "none";
        }
    
        var controlImgWrapper = document.createElement('div');
        controlImgWrapper.style.width = '16px';
        controlImgWrapper.style.height = '16px';
        controlImgWrapper.style.overflow = 'hidden';
        controlImgWrapper.style.display = 'inline-block';
        controlImgWrapper.style.marginTop = '4px';
        controlUI.appendChild(controlImgWrapper);
    
        var imageOffset = {
          "circle": 0,
          "openhand": -9 * 16
        }[image];
    
        var controlImg = document.createElement('img');
        controlImg.src = 'https://maps.gstatic.com/mapfiles/drawing.png';
        controlImg.style.marginTop = imageOffset + "px";
        controlImgWrapper.appendChild(controlImg);
    
        var focusBackground = function() {
          controlUI.style.backgroundColor = '#eee';
        };
    
        var unfocusBackground = function() {
          controlUI.style.backgroundColor = "#fff";
        };
    
        controlImg.addEventListener('mouseenter', focusBackground);
        controlImg.addEventListener('mouseout', unfocusBackground);
        controlUI.addEventListener('mouseenter', focusBackground);
        controlUI.addEventListener('mouseout', unfocusBackground);
    
        return controlUI;
      }
    
      self._controls = {
        circle: createControlUI("Draw a circle", "circle"),
        stop: createControlUI("Stop drawing", "openhand"),
      };
    
      controlDiv.appendChild(self._controls.stop);
      controlDiv.appendChild(self._controls.circle);
    };
    
    FakeDrawer.prototype.distanceBetweenPoints = function(p1, p2) {
      if (!p1 || !p2) {
        return 0;
      }
      var R = 6371;
      var dLat = (p2.lat() - p1.lat()) * Math.PI / 180;
      var dLon = (p2.lng() - p1.lng()) * Math.PI / 180;
      var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
      var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
      var d = R * c;
      return d;
    };
    
    
    
    
    
    function InitializeMap() {
      var latlng = new google.maps.LatLng(29.760193, -95.36939);
      var myOptions = {
        zoom: 12,
        center: latlng,
        zoomControl: true,
        mapTypeId: google.maps.MapTypeId.ROADMAP,
        disableDefaultUI: true
      };
      var map = new google.maps.Map(document.getElementById("map"), myOptions);
    
      /* Add a custom control */
      var fakeDrawerDiv = document.createElement('div');
      var fakeDrawer = new FakeDrawer(fakeDrawerDiv, map);
    
      fakeDrawerDiv.index = 1;
      map.controls[google.maps.ControlPosition.TOP_CENTER].push(fakeDrawerDiv);
    
    
      var updateInfo = function(circle) {
        document.getElementById("info").innerHTML = "Radius: " + circle.getRadius();
      };
    
      google.maps.event.addListener(fakeDrawer, 'drawing_radius_changed', updateInfo);
      google.maps.event.addListener(fakeDrawer, 'circlecomplete', function(circle) {
        google.maps.event.addListener(circle, 'radius_changed', function() {
          updateInfo(circle);
        });
      });
    }
    
    
    google.maps.event.addDomListener(window, 'load', InitializeMap);
    html,
    body {
      height: 100%;
      margin: 0px;
      padding: 0px
    }
    #map {
      height: 80%;
      width: 100%;
    }
    
    

    Step 1: Create a custom control

    Somewhere in the file or as an external library:

    var FakeDrawer = function (controlDiv, map) {
        var self = this;
    
        /* Initialization, some styling ... */
        self._map = map;
        self.initControls(controlDiv);
    };
    
    FakeDrawer.prototype.initControls(controlDiv) {
        var self = this;
    
        function createControlUI (title, image) {
            var controlUI = document.createElement('div');
            /* ... See the snippet for details .. just some styling */
            return controlUI;
        }
    
        self._controls = {
            circle: createControlUI("Draw a circle", "circle"),
            stop: createControlUI("Stop drawing", "openhand"),
         };
    
         controlDiv.appendChild(self._controls.stop);
         controlDiv.appendChild(self._controls.circle);
    };
    

    Step 2: Add some sugars

    This are functions that we may use; Highly inspired from your JsFiddle :)

    A reset method to recover a consistent state when needed:

    FakeDrawer.prototype.reset = function () {
        var self = this;
        self._map.setOptions({
            draggableCursor: "",
            draggable: "true"
        });
    
        /* Remove any applied listener */
        if (self._drawListener) { google.maps.event.removeListener(self._drawListener) ; }
    };
    

    And, a distance computer:

    FakeDrawer.prototype.distanceBetweenPoints = function (p1, p2) {
        if (!p1 || !p2) {
            return 0;
        }
        var R = 6371;
        var dLat = (p2.lat() - p1.lat()) * Math.PI / 180;
        var dLon = (p2.lng() - p1.lng()) * Math.PI / 180;
        var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
        var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        var d = R * c;
        return d;
    };
    

    Step 3: Create your own drawing mode

    Now that we have some controls, we have to define their behavior. The stop control is straightforward; Let's have a look to the circle control.

    FakeDrawer.prototype.drawingMode = function (self) {
        return function (center) {
            /* Let's freeze the map during drawing */
            self._map.setOptions({draggable: false});
    
            /* Create a new circle which will be manually scaled */
            var circle = new google.maps.Circle({
                fillColor: '#000',
                fillOpacity: 0.3,
                strokeWeight: 2,
                clickable: false,
                editable: false,
                map: self._map,
                radius: 1,
                center: center.latLng,
                zIndex: 1
            });
    
            /* Update the radius on each mouse move */
            var onMouseMove = self._map.addListener('mousemove', function (border) {
                var radius = 1000 * self.distanceBetweenPoints(center.latLng, border.latLng);
                circle.setRadius(radius);
    
                /* Here is the feature, know the radius while drawing */
                google.maps.event.trigger(self, 'drawing_radius_changed', circle);
            });
    
            /* The user has finished its drawing */
            google.maps.event.addListenerOnce(self._map, 'mouseup', function () {
                /* Remove all listeners as they are no more required */
                google.maps.event.removeListener(onMouseMove);
    
                circle.setEditable(true);
    
                /* Restore some options to keep a consistent behavior */
                self.reset();
    
                /* Notify listener with the final circle */
                google.maps.event.trigger(self, 'circlecomplete', circle);
            });
        };
    };
    

    Step 4: Bind controls

    Now that everything is okay, let's add some listeners to the initial version of the constructor so that each control has a corresponding action when clicked.

    var FakeDrawer = function (controlDiv, map) {
        var self = this;
    
        /* Initialization, some styling ... */
        self._map = map;
        self.initControls(controlDiv);
    
        /* Setup the click event listeners: drawingmode */
        google.maps.event.addDomListener(self._controls.circle, 'click', function() {
            /* Ensure consistency */
            self.reset();
    
            /* Only drawingmode */
            self._map.setOptions({draggableCursor: "crosshair"});
            self._drawListener = self._map.addListener('mousedown', self.drawingMode(self));
        });
    
        google.maps.event.addDomListener(self._controls.stop, 'click', function () {
            self.reset();
        });
    };
    

    Step 5: Use it!

    Assuming that your map has been initialized correctly.

    Inside your map init function:

    var fakeDrawerDiv = document.createElement('div');
    var fakeDrawer = new FakeDrawer(fakeDrawerDiv, map);
    
    fakeDrawerDiv.index = 1;
    map.controls[google.maps.ControlPosition.TOP_CENTER].push(fakeDrawerDiv);
    
    var updateInfo = function (circle) {
        document.getElementById("info").innerHTML = "Radius: " + circle.getRadius();
    };
    
    google.maps.event.addListener(fakeDrawer, 'drawing_radius_changed', updateInfo);
    google.maps.event.addListener(fakeDrawer, 'circlecomplete', function (circle) {
        google.maps.event.addListener(circle, 'radius_changed', function () {
            updateInfo(circle);
        });
    });
    

    Enjoy, hope it will help.

    0 讨论(0)
提交回复
热议问题