Pages

Friday, January 20, 2012

Google Maps: Dynamically Movable and Resizable Circle Overlay


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 athttp://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:  
Google Map Dynamic Circle
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&amp;v=2&amp;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 Circle Center marker 
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. 
<body onload="initialize()" onunload="GUnload()"> 
<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!

No comments:

Post a Comment