Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Maps JS API - draw resizable circle with a static center

I have a requirement to draw a circle on google maps from a given static point. I tried to accomplish this by setting the circle setting center as follows, center: new google.maps.LatLng(-34.397, 150.644) after being drawn using the DrawingManager.

But the issue here is the circle starts to draw from the mouse click location and once done the circle snaps into the given lat and lng as demo-ed in the fiddle : http://jsfiddle.net/hjkety80/1/

What I require is, regardless of where I click the circle should start drawing from the static point given. I apologize if the description is vague and would glad to clarify more if required. Any help / reference on this matter is greatly appreciated.

Thanks

EDIT

Even if the circle center binds to a marker is also a good workaround if possible

like image 846
Hasitha Shan Avatar asked May 20 '16 04:05

Hasitha Shan


People also ask

How do I create a radius in Google Maps?

Click on the map and create a popup marker to select the point. From there, opt for the “Draw Radius.” Choose the proximity distance from the given address found within the radius options in the software. Once settings are entered, a map will show the highlighted parameters on the map.

How do I draw multiple circles on Google Maps?

Click anywhere on the map to create a circle. You may continue clicking outside the circles to create more circles. You can reposition (drag) the circles with your mouse by clicking+holding+moving.


2 Answers

I have created a working example for you. It's not using DrawingManager because it's not suitable for what you want to achieve, instead I update the radius of the circle programatically.

So in this example, when map is loaded, user is presented with a pointer cursor and when he presses the mouse button, he will start drawing a circle with a center in your static_position and the radius of the new circle is determined based on where he clicked.

The example includes geometry library (notice &libraries=geometry when fetching the Google maps API) and leverages it's google.maps.geometry.spherical.computeDistanceBetween() function to calculate distance in meters between your center and where user just clicked to get radius. If user doesn't immediately release the button and instead moves the mouse around, the circle will automatically adjust it's radius based on user movements with cursor. After the user releases the mouse, he can drag the map around and also circle stays editable for adjustments.

The example (Just press left mouse button on the map to start drawing. For better experience run in full page mode):

var map = null;

function initialize(){
	
  var static_position = new google.maps.LatLng(-34.397, 150.644);
  var the_circle = null;
 
  map = new google.maps.Map(document.getElementById('map-canvas'), {
      center: static_position,
      zoom: 8,
      draggable: false,
      draggableCursor:'pointer'
  });
  
  var mousemove_handler;
  
  google.maps.event.addListener(map, 'mouseup', function(e) {            
      if(mousemove_handler) google.maps.event.removeListener(mousemove_handler);
      map.setOptions({draggable:true, draggableCursor:''}); //allow map dragging after the circle was already created 
      the_circle.setOptions({clickable:true});
  });
  
  google.maps.event.addListenerOnce(map, 'mousedown', function (mousedown_event) {
	  var radius = google.maps.geometry.spherical.computeDistanceBetween(static_position, mousedown_event.latLng); //get distance in meters between our static position and clicked position, which is the radius of the circle
	  the_circle = createCircle(static_position, radius); //create circle with center in our static position and our radius
	  mousemove_handler = google.maps.event.addListener(map, 'mousemove', function(mousemove_event) { //if after mousedown user starts dragging mouse, let's update the radius of the new circle
		  var new_radius = google.maps.geometry.spherical.computeDistanceBetween(static_position, mousemove_event.latLng);
		  console.log(new_radius);
		  the_circle.setOptions({radius:new_radius}); 
	  });
  });
  
}

google.maps.event.addDomListener(window, 'load', initialize);

function createCircle(center, radius) {

    var circle = new google.maps.Circle({
        fillColor: '#ffffff',
        fillOpacity: .6,
        strokeWeight: 1,
        strokeColor: '#ff0000',
        draggable: false,
        editable: true,
        map: map,
        center: center,
        radius: radius,
        clickable:false
    });

    google.maps.event.addListener(circle, 'radius_changed', function (event) {
        console.log('circle radius changed');
    });

    google.maps.event.addListener(circle, 'center_changed', function (event) {
        if(circle.getCenter().toString() !== center.toString()) circle.setCenter(center);
    });
	
    return circle;
}
<script src="//maps.google.com/maps/api/js?sensor=false&libraries=geometry&dummy=.js"></script>
<body style="margin:0px; padding:0px;">
	 <div id="map-canvas" style="height:400px; width:500px;"></div> 
</body>
like image 63
Matej P. Avatar answered Oct 14 '22 17:10

Matej P.


One option (from my answer to this question: How to style editable circle controls in Google Maps). DistanceWidget with a fixed center marker.

related questions:

  • How to style editable circle controls in Google Maps
  • Get radius while resizing circle

code snippet:

function init() {
  var mapDiv = document.getElementById('map-canvas');
  var map = new google.maps.Map(mapDiv, {
    center: new google.maps.LatLng(-34.397, 150.644),
    zoom: 8,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  });
  var distanceWidget = new DistanceWidget(map);
  google.maps.event.addListener(distanceWidget, 'distance_changed', function() {
    displayInfo(distanceWidget);
  });

  google.maps.event.addListener(distanceWidget, 'position_changed', function() {
    displayInfo(distanceWidget);
  });
}

google.maps.event.addDomListener(window, 'load', init);

/** 
 * A distance widget that will display a circle that can be resized and will
 * provide the radius in km.
 *
 * @param {google.maps.Map} map The map on which to attach the distance widget.
 *
 * @constructor
 */
function DistanceWidget(map) {
  this.set('map', map);
  this.set('position', map.getCenter());

  var marker = new google.maps.Marker({
    draggable: false,
    icon: {
      url: "https://maps.gstatic.com/intl/en_us/mapfiles/markers2/measle_blue.png",
      size: new google.maps.Size(7, 7),
      anchor: new google.maps.Point(4, 4)
    },
    title: 'Move me!'
  });

  // Bind the marker map property to the DistanceWidget map property 
  marker.bindTo('map', this);

  // Bind the marker position property to the DistanceWidget position 
  // property 
  marker.bindTo('position', this);

  // Create a new radius widget 
  var radiusWidget = new RadiusWidget();

  // Bind the radiusWidget map to the DistanceWidget map 
  radiusWidget.bindTo('map', this);

  // Bind the radiusWidget center to the DistanceWidget position 
  radiusWidget.bindTo('center', this, 'position');

  // Bind to the radiusWidgets' distance property 
  this.bindTo('distance', radiusWidget);

  // Bind to the radiusWidgets' bounds property 
  this.bindTo('bounds', radiusWidget);
}
DistanceWidget.prototype = new google.maps.MVCObject();

/** 
 * A radius widget that add a circle to a map and centers on a marker.
 *
 * @constructor
 */
function RadiusWidget() {
  var circle = new google.maps.Circle({
    strokeWeight: 2
  });

  // Set the distance property value, default to 50km. 
  this.set('distance', 50);

  // Bind the RadiusWidget bounds property to the circle bounds property. 
  this.bindTo('bounds', circle);

  // Bind the circle center to the RadiusWidget center property 
  circle.bindTo('center', this);

  // Bind the circle map to the RadiusWidget map 
  circle.bindTo('map', this);

  // Bind the circle radius property to the RadiusWidget radius property 
  circle.bindTo('radius', this);

  this.addSizer_();
}
RadiusWidget.prototype = new google.maps.MVCObject();


/** 
 * Update the radius when the distance has changed.
 */
RadiusWidget.prototype.distance_changed = function() {
  this.set('radius', this.get('distance') * 1000);
};
/** 
 * Add the sizer marker to the map.
 *
 * @private
 */
RadiusWidget.prototype.addSizer_ = function() {
  var sizer = new google.maps.Marker({
    draggable: true,
    icon: {
      url: "https://maps.gstatic.com/intl/en_us/mapfiles/markers2/measle_blue.png",
      size: new google.maps.Size(7, 7),
      anchor: new google.maps.Point(4, 4)
    },
    title: 'Drag me!'
  });

  sizer.bindTo('map', this);
  sizer.bindTo('position', this, 'sizer_position');

  var me = this;
  google.maps.event.addListener(sizer, 'drag', function() {
    // Set the circle distance (radius) 
    me.setDistance();
  });
};

/** 
 * Update the center of the circle and position the sizer back on the line.
 *
 * Position is bound to the DistanceWidget so this is expected to change when
 * the position of the distance widget is changed.
 */
RadiusWidget.prototype.center_changed = function() {
  var bounds = this.get('bounds');

  // Bounds might not always be set so check that it exists first. 
  if (bounds) {
    var lng = bounds.getNorthEast().lng();

    // Put the sizer at center, right on the circle. 
    var position = new google.maps.LatLng(this.get('center').lat(), lng);
    this.set('sizer_position', position);
  }
};

/** 
 * Calculates the distance between two latlng locations in km.
 * @see http://www.movable-type.co.uk/scripts/latlong.html
 *
 * @param {google.maps.LatLng} p1 The first lat lng point.
 * @param {google.maps.LatLng} p2 The second lat lng point.
 * @return {number} The distance between the two points in km.
 * @private
 */
RadiusWidget.prototype.distanceBetweenPoints_ = function(p1, p2) {
  if (!p1 || !p2) {
    return 0;
  }

  var R = 6371; // Radius of the Earth in km 
  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;
};


/** 
 * Set the distance of the circle based on the position of the sizer.
 */
RadiusWidget.prototype.setDistance = function() {
  // As the sizer is being dragged, its position changes.  Because the 
  // RadiusWidget's sizer_position is bound to the sizer's position, it will 
  // change as well. 
  var pos = this.get('sizer_position');
  var center = this.get('center');
  var distance = this.distanceBetweenPoints_(center, pos);

  // Set the distance property for any objects that are bound to it 
  this.set('distance', distance);
};

function displayInfo(widget) {
  var info = document.getElementById('info');
  info.innerHTML = 'Position: ' + widget.get('position').toUrlValue(3) + ', distance: ' + widget.get('distance').toFixed(3);
}
html,
body,
#map-canvas {
  height: 100%;
  margin: 0px;
  padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js?v=3&libraries=geometry&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<div id="info"></div>
<div id="map-canvas"></div>
like image 33
geocodezip Avatar answered Oct 14 '22 17:10

geocodezip