In extension to my previous post on Dynamically Movable and Resizable Circle Overlay, I created yet another nifty tool to dynamically draw a polygon overlay on Google Maps. You won’t find another robust tool like this for Polygon drawing on the internet. Users can draw polygon dynamically by adding, deleting and moving the polygon boundary nodes. User can press shift+click on the map to add a new node to polygon boundary or they can press ctrl+click on any node marker to remove it. The node markers can be dragged around to resize the polygon.
This is how it looks like in action:
The JavaScript 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. A click event is also added to the map for adding node markers on the map. The click event takes care of the case when the user click on the previous markers while holding the shift key. When the user clicks on the map, addMarker() function is called to add another boundary node to the polygon. For every newly added marker, drag events are assigned for resizing the polygon and a click event is assigned to delete the node when ctrl key is pressed while clicking the marker pin. The fitPolygon() function is used to set map bounds and center to adjust the polygon inside it.
Put the following JavaScript code in the head section of your page:
<script type="text/javascript">
/* Developed by: Abhinay Rathore [web3o.blogspot.com] */
//Global variables
var global = this;
var map;
var PolygonMarkers = []; //Array for Map Markers
var PolygonPoints = []; //Array for Polygon Node Markers
var bounds = new GLatLngBounds; //Polygon Bounds
var Polygon; //Polygon overlay object
var polygon_resizing = false; //To track Polygon Resizing
//Polygon 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(new GLatLng(38.90, -94.68007), 13);
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
//Add Shift+Click event to add Polygon markers
GEvent.addListener(map, "click", function(overlay, point, overlaypoint) {
var p = (overlaypoint) ? overlaypoint : point;
//Add polygon marker if overlay is not an existing marker and shift key is pressed
if (global.shiftKey && !checkPolygonMarkers(overlay)) { addMarker(p); }
});
}
}
// Adds a new Polygon boundary marker
function addMarker(point) {
var markerOptions = { icon: bluepin, draggable: true };
var marker = new GMarker(point, markerOptions);
PolygonMarkers.push(marker); //Add marker to PolygonMarkers array
map.addOverlay(marker); //Add marker on the map
GEvent.addListener(marker,'dragstart',function(){ //Add drag start event
marker.setImage(redpin.image);
polygon_resizing = true;
});
GEvent.addListener(marker,'drag',function(){ drawPolygon(); }); //Add drag event
GEvent.addListener(marker,'dragend',function(){ //Add drag end event
marker.setImage(bluepin.image);
polygon_resizing = false;
drawPolygon();
fitPolygon();
});
GEvent.addListener(marker,'click',function(point) { //Add Ctrl+Click event to remove marker
if (global.ctrlKey) { removeMarker(point); }
});
drawPolygon();
//If more then 2 nodes then automatically fit the polygon
if(PolygonMarkers.length > 2) fitPolygon();
}
// Removes a Polygon boundary marker
function removeMarker(point) {
if(PolygonMarkers.length == 1){ //Only one marker in the array
map.removeOverlay(PolygonMarkers[0]);
map.removeOverlay(PolygonMarkers[0]);
PolygonMarkers = [];
if(Polygon){map.removeOverlay(Polygon)};
}
else //More then one marker
{
var RemoveIndex = -1;
var Remove;
//Search for clicked Marker in PolygonMarkers Array
for(var m=0; m<PolygonMarkers.length; m++)
{
if(PolygonMarkers[m].getPoint().equals(point))
{
RemoveIndex = m; Remove = PolygonMarkers[m]
break;
}
}
//Shift Array elemeents to left
for(var n=RemoveIndex; n<PolygonMarkers.length-1; n++)
{
PolygonMarkers[n] = PolygonMarkers[n+1];
}
PolygonMarkers.length = PolygonMarkers.length-1 //Decrease Array length by 1
map.removeOverlay(Remove); //Remove Marker
drawPolygon(); //Redraw Polygon
}
}
//Draw Polygon from the PolygonMarkers Array
function drawPolygon()
{
PolygonPoints.length=0;
for(var m=0; m<PolygonMarkers.length; m++)
{
PolygonPoints.push(PolygonMarkers[m].getPoint()); //Add Markers to PolygonPoints node array
}
//Add first marker in the end to close the Polygon
PolygonPoints.push(PolygonMarkers[0].getPoint());
if(Polygon){ map.removeOverlay(Polygon); } //Remove existing Polygon from Map
var fillColor = (polygon_resizing) ? 'red' : 'blue'; //Set Polygon Fill Color
Polygon = new GPolygon(PolygonPoints, '#FF0000', 2, 1, fillColor, 0.2); //New GPolygon object
map.addOverlay(Polygon); //Add Polygon to the Map
//TO DO: Function Call triggered after Polygon is drawn
}
//Fits the Map to Polygon bounds
function fitPolygon(){
bounds = Polygon.getBounds();
map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
}
//check is the marker is a polygon boundary marker
function checkPolygonMarkers(marker) {
var flag = false;
for (var m = 0; m < PolygonMarkers.length; m++) {
if (marker == PolygonMarkers[m])
{ flag = true; break; }
}
return flag;
}
//////////////////[ Key down event handler ]/////////////////////
//Event handler class to attach events
var EventUtil = {
addHandler: function(element, type, handler){
if (element.addEventListener){
element.addEventListener(type, handler, false);
} else if (element.attachEvent){
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
}
};
// Attach Key down/up events to document
EventUtil.addHandler(document, "keydown", function(event){keyDownHandler(event)});
EventUtil.addHandler(document, "keyup", function(event){keyUpHandler(event)});
//Checks for shift and Ctrl key press
function keyDownHandler(e)
{
if (!e) var e = window.event;
var target = (!e.target) ? e.srcElement : e.target;
if (e.keyCode == 16 && !global.shiftKey) { //Shift Key
global.shiftKey = true;
}
if (e.keyCode == 17 && !global.ctrlKey) { //Ctrl Key
global.ctrlKey = true;
}
}
//Checks for shift and Ctrl key release
function keyUpHandler(e){
if (!e) var e = window.event;
if (e.keyCode == 16 && global.shiftKey) { //Shift Key
global.shiftKey = false;
}
if (e.keyCode == 17 && global.ctrlKey) { //Ctrl Key
global.ctrlKey = false;
}
}
</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!