Google provides a pretty wholesome Map API for creating customized maps with enhanced features. In a recent project of mine I had to use dynamically drawn map overlays for searching house properties within them. To implement that I looked for some algorithms to draw circles on Google Map Overlays and found a good one at http://koti.mbnet.fi/ojalesa/googlepages/circle.htm, but this program was built to change the radius using html form field. To take it a few steps further, I planned to make this circle movable and resizable by directly dragging map icons.
This is one of the best dynamic circle overlay drawing tool you can find on internet. There are two pin markers to alter the circle, the Blue pin is used to drag the circle around on the map and the Red one can be dragged to resize the circle. You can even set minimum and maximum radius for the circle in the JavaScript code. The resize marker pin always stays at the 0 degree edge of the circle. The Circle fill color changes to red if the circle is moved or resized.
This is how it looks like in action:

JavaScript code: The logistic code is pretty easy to modify for your custom needs. The initialize() function is used to initialize the Google Map object when the page loads. Circle Center and Resize markers are then added at the default center location. Marker drag events are assigned to Center and Resize markers for dragging and resizing the circle. The drawCircle() function implements the algorithm for drawing the circle and finally the fitCircle() function is used to set the Map bounds to include the full circle inside it. If you want to trigger any other function after the circle is drawn, you can call your function after the fitCircle() function call at the end of drawCircle() function. Google Map uses Metric Units for distance by default, you will have to use conversion factor (1km = 0.621371192mi) to convert radius value into miles if you wish to.
Place the following JavaScript code in the head section of your page:
<script src="http://maps.google.com/maps?file=api&v=2&key=YOUR_API_KEY&sensor=true" type="text/javascript"></script>
<script type="text/javascript">
/* Developed by: Abhinay Rathore [web3o.blogspot.com] */
//Global variables
var map;
var bounds = new GLatLngBounds; //Circle Bounds
var map_center = new GLatLng(38.903843, -94.680096);
var Circle; //Circle object
var CirclePoints = []; //Circle drawing points
var CircleCenterMarker, CircleResizeMarker;
var circle_moving = false; //To track Circle moving
var circle_resizing = false; //To track Circle resizing
var radius = 1; //1 km
var min_radius = 0.5; //0.5km
var max_radius = 5; //5km
//Circle Marker/Node icons
var redpin = new GIcon(); //Red Pushpin Icon
redpin.image = "http://maps.google.com/mapfiles/ms/icons/red-pushpin.png";
redpin.iconSize = new GSize(32, 32);
redpin.iconAnchor = new GPoint(10, 32);
var bluepin = new GIcon(); //Blue Pushpin Icon
bluepin.image = "http://maps.google.com/mapfiles/ms/icons/blue-pushpin.png";
bluepin.iconSize = new GSize(32, 32);
bluepin.iconAnchor = new GPoint(10, 32);
function initialize() { //Initialize Google Map
if (GBrowserIsCompatible()) {
map = new GMap2(document.getElementById("map_canvas")); //New GMap object
map.setCenter(map_center);
var ui = new GMapUIOptions(); //Map UI options
ui.maptypes = { normal:true, satellite:true, hybrid:true, physical:false }
ui.zoom = {scrollwheel:true, doubleclick:true};
ui.controls = { largemapcontrol3d:true, maptypecontrol:true, scalecontrol:true };
map.setUI(ui); //Set Map UI options
addCircleCenterMarker(map_center);
addCircleResizeMarker(map_center);
drawCircle(map_center, radius);
}
}
// Adds
function addCircleCenterMarker(point) {
var markerOptions = { icon: bluepin, draggable: true };
CircleCenterMarker = new GMarker(point, markerOptions);
map.addOverlay(CircleCenterMarker); //Add marker on the map
GEvent.addListener(CircleCenterMarker, 'dragstart', function() { //Add drag start event
circle_moving = true;
});
GEvent.addListener(CircleCenterMarker, 'drag', function(point) { //Add drag event
drawCircle(point, radius);
});
GEvent.addListener(CircleCenterMarker, 'dragend', function(point) { //Add drag end event
circle_moving = false;
drawCircle(point, radius);
});
}
// Adds Circle Resize marker
function addCircleResizeMarker(point) {
var resize_icon = new GIcon(redpin);
resize_icon.maxHeight = 0;
var markerOptions = { icon: resize_icon, draggable: true };
CircleResizeMarker = new GMarker(point, markerOptions);
map.addOverlay(CircleResizeMarker); //Add marker on the map
GEvent.addListener(CircleResizeMarker, 'dragstart', function() { //Add drag start event
circle_resizing = true;
});
GEvent.addListener(CircleResizeMarker, 'drag', function(point) { //Add drag event
var new_point = new GLatLng(map_center.lat(), point.lng()); //to keep resize marker on horizontal line
var new_radius = new_point.distanceFrom(map_center) / 1000; //calculate new radius
if (new_radius < min_radius) new_radius = min_radius;
if (new_radius > max_radius) new_radius = max_radius;
drawCircle(map_center, new_radius);
});
GEvent.addListener(CircleResizeMarker, 'dragend', function(point) { //Add drag end event
circle_resizing = false;
var new_point = new GLatLng(map_center.lat(), point.lng()); //to keep resize marker on horizontal line
var new_radius = new_point.distanceFrom(map_center) / 1000; //calculate new radius
if (new_radius < min_radius) new_radius = min_radius;
if (new_radius > max_radius) new_radius = max_radius;
drawCircle(map_center, new_radius);
});
}
//Draw Circle with given radius and center
function drawCircle(center, new_radius) {
//Circle Drawing Algorithm from: http://koti.mbnet.fi/ojalesa/googlepages/circle.htm
//Number of nodes to form the circle
var nodes = new_radius * 40;
if(new_radius < 1) nodes = 40;
//calculating km/degree
var latConv = center.distanceFrom(new GLatLng(center.lat() + 0.1, center.lng())) / 100;
var lngConv = center.distanceFrom(new GLatLng(center.lat(), center.lng() + 0.1)) / 100;
CirclePoints = [];
var step = parseInt(360 / nodes) || 10;
var counter = 0;
for (var i = 0; i <= 360; i += step) {
var cLat = center.lat() + (new_radius / latConv * Math.cos(i * Math.PI / 180));
var cLng = center.lng() + (new_radius / lngConv * Math.sin(i * Math.PI / 180));
var point = new GLatLng(cLat, cLng);
CirclePoints.push(point);
counter++;
}
CircleResizeMarker.setLatLng(CirclePoints[Math.floor(counter / 4)]); //place circle resize marker
CirclePoints.push(CirclePoints[0]); //close the circle polygon
if (Circle) { map.removeOverlay(Circle); } //Remove existing Circle from Map
var fillColor = (circle_resizing || circle_moving) ? 'red' : 'blue'; //Set Circle Fill Color
Circle = new GPolygon(CirclePoints, '#FF0000', 2, 1, fillColor, 0.2); //New GPolygon object for Circle
map.addOverlay(Circle); //Add Circle Overlay on the Map
radius = new_radius; //Set global radius
map_center = center; //Set global map_center
if (!circle_resizing && !circle_moving) { //Fit the circle if it is nor moving or resizing
fitCircle();
//Circle drawing complete trigger function goes here
}
}
//Fits the Map to Circle bounds
function fitCircle() {
bounds = Circle.getBounds();
map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
}
</script>
To initialize the map you can call the functions on page load event and include a div tag inside your body to hold the Map.
<div id="map_canvas" style="width:100%; height:450px"></div>
</body>
Feel free to modify and use this code on your website. I have used API v2 for my code but you can easily modify this code for API v3. Happy Mapping!
I really appriciate - this is exactly what i need - you saved my hours - thanks
ReplyDeleteHi, Great.. Tutorial is just awesome..It is really helpful for a newbie like me.. I am a regular follower of your blog. Really very informative post you shared here. Kindly keep blogging. If anyone wants to become a Front end developer learn from Javascript Training in Chennai . or Javascript Training in Chennai. Nowadays JavaScript has tons of job opportunities on various vertical industry. ES6 Training in Chennai
DeleteNice work!
ReplyDeleteBut how do I modify it to API v3?
Appreciate all help to get this code in a v3 version as I'm not anything close to beeing a javascript guru :-)
/Michael
Pls give some feedback on my website.....
ReplyDeletethanks
want c# code instead of javascript .........
ReplyDeleteThanks a lot for your valuable code..really one code which can be directly run after copying it from the internet.
ReplyDeleteI absolutely love your site.. Excellent colors & theme. Did you create this
ReplyDeletewebsite yourself? Please reply back as I'm hoping to create my own blog and would like to find out where you got this from or what the theme is named. Thanks!
My web page : adwords
Excellent job,
ReplyDeleteThanks a lot dear
anybody has migrated to V3? can you please share the code?
ReplyDeleteThat was awesome work, Now iam having multiple circle(location) in the same map, i want the same concept and method to be work for all the circle, default location will be the center location of the map. Kindly help me in this.
ReplyDeleteIs there a V3 version?
ReplyDeleteMine doesn't work.....
ReplyDeleteGreat job man can i get v3 version.
ReplyDeleteGood work, but the auto resizing and recentering of the map is super annoying and I can't figure out how to disable it.
ReplyDeletethis is exactly what i want.but i need this one in apiv3,how can i convert this one into v3????
ReplyDeletecan i get circle on click not drag it?
ReplyDeletehow to set custom marker in this examle
ReplyDelete