Make a polygon using Google Maps API v3

Polygon using Google Maps

Google Maps API v3 makes it really easy for us to make polygons on the map. In this post we will look at an example of creating polygon using Google Maps.

Requirements

  • create a polygon using Google’s drawing tools. After drawing the polygon, drawing tools should disappear
  • click on the polygon to show a dialog box, click edit to edit polygon
  • click on the polygon to show a dialog box, click done to save the edits
  • click on the polygon to show a dialog box, click delete to delete the polygon, and then show drawing tools to draw another dialog box

Final Result

The final result is available on Make a polygon page. Feel free to click on the polygon drawing tool and click on different parts of the map. Join back to where you started or double click to allow Google Maps to finish the polygon automatically.

Entire solution – so concise!

Let’s see how it is all put together. Feel free to create your own web page using the following code. You are free to download map-polygon-example.js to complete your own example. The Javascript file is well documented. The code is also on https://github.com/zedfoxus/google-maps-demo and a full-screen version is available also http://zedfox.us/projects/google-maps-demo/make-polygon/.

<html>
<head></head>
<body>
    <p id="map-canvas" style="height: 300px;"></p>
    <style>
      img { max-width:100%; }
      #map-canvas img { max-width:none; }
    </style>
    <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.exp&amp;sensor=false&amp;libraries=drawing,geometry"></script>
    <script type="text/javascript" src="/wp-content/uploads/2014/03/map-polygon-example.js"></script>
</body>
</html>

Details of the functionality in Javascript map-polygon-example.js is included below.

Initialization function

Let’s understand the functioning of the Javascript. We begin with creation of the core items we will need in the mapping process.

    // declare variables that will be used in this example
    var myMap;                  // holds the map object drawn on the 
    var myDrawingManager;       // holds drawing tools
    var myField;                // holds the polygon we draw using drawing tools
    var myInfoWindow;           // when our polygon is clicked, a dialog box 
                                // will open up. This variable holds that info
    var centerpoint;            // center point of the map

    (function(){ initialize(); })();

The last line invokes the function named initialize immediately. Let’s see what initialize does.

    /**
     * Initialization function that sets up the map
     */
    function initialize() {
        // build the map's center point
        centerpoint = new google.maps.LatLng(45.00495,-90.00052);

        // assign map the options of zoom, center point and set the map to
        // SATELLITE
        var mapOptions = {
            zoom: 16, 
            center: centerpoint, 
            mapTypeId: google.maps.MapTypeId.SATELLITE
        };

        // on our web page should be a <div> or <p> tag with id map-canvas
        // show the map in that element with the options listed above
        myMap = new google.maps.Map(
            document.getElementById('map-canvas'), 
            mapOptions
        );

        // create a dialog box but don't bind it to anything yet
        myInfoWindow = new google.maps.InfoWindow();

        // show drawing tools
        DrawingTools();
    }

The initialize function begins with setting up a center point for the map and some options. There are several mapOptions that can be configured during startup. We have chosen to set the mapTypeID to SATELLITE but you can choose from HYBRID, TERRAIN, ROADMAP or SATELLITE. Once these options are created, we will as Google Map API to build us a map and stick the map into <p id=”map-canvas” style=”height: 300px;”></p> tag. While we are at it, we will create a variable to hold dialog boxes when our polygons will be clicked.

Drawing tools

Building drawing tools is rather easy. Google makes it very easy for us to build them like so:

    /**
     * Show drawing tools
     */
    function DrawingTools() {

        // drawingMode of NULL, which means that the map drawing tools will
        // have no default drawing tool selected. If drawingMode was set to 
        // google.maps.drawing.OverlayType.POLYGON, polygon would be auto-
        // selected
        // drawingModes can have multiple information. Over here only the
        // polygon capability is added along with the default of hand icon
        // Moreover, polygonOptions are specified as defaults
        myDrawingManager = new google.maps.drawing.DrawingManager({
            drawingMode: null,
            drawingControl: true,
            drawingControlOptions: {
                position: google.maps.ControlPosition.TOP_RIGHT,
                drawingModes: [
                    google.maps.drawing.OverlayType.POLYGON
                ]
            },
            polygonOptions: {
                draggable: true,
                editable: true,
                fillColor: '#cccccc',
                fillOpacity: 0.5,
                strokeColor: '#000000',
            }
        });
        myDrawingManager.setMap(myMap);

        // when polygon drawing is complete, an event is raised by the map
        // this function will listen to the event and work appropriately
        FieldDrawingCompletionListener();
    }

DrawingManager class provides many outstanding options. We can allow users to create MARKER, CIRCLE, RECTANGLE, POLYLINE and POLYGON. The hand icon is there by default to drag the map around and general navigation. In the function above, the polygon creation icon will be displayed along with the hand icon. Not only that, we have customized properties of the polygon that can be created. Once these options are set, we attach the drawing manager to the map we created.

At this point the user will be able to click on the polygon icon and create a polygon easily by marking points on the map and double-clicking on the map to auto-complete the polygon. Once the polygon is created, I’d like to do different things such as hide the drawing manager, pop open a dialog box containing instructions to edit/save/delete the polygon. First things first – let’s do something so that the polygon listens to click events.

Drawing manager – detect completion of polygon

Drawing manager generates an event when polygon drawing is complete. We leverage that event by listening on it and once polygoncomplete event is received, we:

  • hide drawing manager
  • save the polygon’s shape and disallow dragging it or editing it
  • add custom object to the polygon
  • attach a listener
    /**
     * Using the drawing tools, when a polygon is drawn an event is raised. 
     * This function catches that event and hides the drawing tool. It also
     * makes the polygon non-draggable and non-editable. It adds custom 
     * properties to the polygon and generates a listener to listen to click
     * events on the created polygon
     */
    function FieldDrawingCompletionListener() {
        // capture the field, set selector back to hand, remove drawing
        google.maps.event.addListener(
            myDrawingManager,
            'polygoncomplete',
            function(polygon) {
                myField = polygon;
                ShowDrawingTools(false);
                PolygonEditable(false);
                AddPropertyToField();
                FieldClickListener();
            }
        );
    }

Drawing manager – hide or show

It is simple to hide or show the drawing manager. Setting the drawing manager’s drawingControl property to true will show the drawing manager, and false will hide it. The following function can be called as ShowDrawingTools(true) to show drawing manager, and ShowDrawingTools(false) to hide it.

    /**
     * Show or hide drawing tools
     */
    function ShowDrawingTools(val) {
        myDrawingManager.setOptions({
            drawingMode: null,
            drawingControl: val
        });
    }

Newly created polygon – disable/enable editing and dragging

It is desired by our application to disable the polygon editing and dragging capabilities once the polygon is created. It is trivial to do so. After an object is created, such as myField polygon object, the object’s settings can be changed by calling the setOptions method and passing appropriate key/value property pairs. Below is a function that will enable or disable dragging and editing of a polygon.

    /**
     * Allow or disallow polygon to be editable and draggable 
     */
    function PolygonEditable(val) {
        myField.setOptions({
            editable: val,
            draggable: val
        });
        myInfoWindow.close();
        return false;
    }

Add custom properties to a polygon

Creation of a polygon is beneficial but addition of properties to a polygon adds power to the application. Javascript has everything as objects. Properties can be added seamlessly. We can create a standalone object with properties and attach it to myField polygon like the code shows below. This way, metadata can be associated with the polygon.

    /**
     * Add custom property to the polygon
     */
    function AddPropertyToField() {
        var obj = {
            'id':5,
            'grower':'Joe',
            'farm':'Dream Farm'
        };
        myField.objInfo = obj;
    }

Attach click event to polygon

It is desired that when a user clicks on the polygon a dialog box opens up with links to edit the polygon, save the polygon using a link called ‘Done’ and delete the polygon. Let’s attach a click event to the polygon. google.maps.event.addListener takes the polygon, event type and a function as arguments. We attach click event to myField. When the event is caught, a function executes that builds appropriate message and uses that in the invoked dialog box myInfoWindow where the user clicked. myInfoWindow’s setPosition method can be used to set the position where the dialog box will show up.

    /**
     * Attach an event listener to the polygon. When a user clicks on the 
     * polygon, get a formatted message that contains links to re-edit the 
     * polygon, mark the polygon as complete, or delete the polygon. The message
     * appears as a dialog box
     */
    function FieldClickListener() {
        google.maps.event.addListener(
            myField,
            'click',
            function(event) {
                var message = GetMessage(myField);
                myInfoWindow.setOptions({ content: message });
                myInfoWindow.setPosition(event.latLng);
                myInfoWindow.open(myMap);
            }
        );
    }

Delete polygon

Deleting the polygon should detach myField polygon from the map after closing any open dialog boxes, and then enable the drawing controls so that another polygon can be drawn. That is accomplished using the code below:

    /**
     * Delete the polygon and show the drawing tools so that new polygon can be
     * created
     */
    function DeleteField() {
        myInfoWindow.close();
        myField.setMap(null);
        ShowDrawingTools(true);
    }

Calculate area of the polygon

    /**
     * Get area of the drawn polygon in acres
     */
    function GetArea(poly) {
        var result = parseFloat(google.maps.geometry.spherical.computeArea(poly.getPath())) * 0.000247105;
        return result.toFixed(4);
    }

Google Maps API already has a function called google.maps.geometry.spherical.computeArea that takes polygon’s path as a parameter and returns area in square meters. The above function leverages this function and returns area in acres instead of square meters.

Message show in the dialog box

The message shown the dialog box will have 3 links: Edit, Done, Delete. Clicking the edit link should allow editing and dragging of the polygon. This can be easily done by the PolygonEditable method we have already described above. Sending true to this method will enable editing and dragging of the polygon. It will also disable the controls so that another polygon cannot be drawn in parallel to editing the existing one.

When Done is clicked, polygon editing and dragging is disabled. This is trivial and can be done by sending false to PolygonEditable method.

Delete link will call the method above to delete the polygon. With all the supporting methods in place, the links can be generated. Since the polygon has an object associated with it that carries metadata, one can display the metadata in the dialog box also. Leveraging GetArea method allow us to provide area to the user in the dialog box as well.

Polygons can have one or more paths. The first path is the boundary of the polygon. If there are more paths, they would either indicate polygons inside the polygon, or holes in the polygon. Polygon’s getPath method returns the first path. Path’s getArray method will give an object containing lat and lon as properties for each vertex. Vertex (plural – vertices) is the corner points that connects lines/sides in the polygon. Let’s put our understanding together to create a message that will show up in a dialog box.

    /**
     * Get coordinates of the polygon and display information that should 
     * appear in the polygon's dialog box when it is clicked
     */
    function GetMessage(polygon) {
        var coordinates = polygon.getPath().getArray();
        var message = '';

        if (typeof myField != 'undefined') {
            message += '<h1 style="color:#000">Grower: ' 
                + myField.objInfo.grower + '<br>'
                + 'Farm: ' + myField.objInfo.farm + '</h1>';
        }

        message += '<div style="color:#000">This polygon has ' 
            + coordinates.length + ' points<br>'
            + 'Area is ' + GetArea(polygon) + ' acres</div>';

        var coordinateMessage = '<p style="color:#000">My coordinates are:<br>';
        for (var i = 0; i < coordinates.length; i++) {
            coordinateMessage += coordinates[i].lat() + ', ' 
                + coordinates[i].lng() + '<br>';
        }
        coordinateMessage += '</p>';

        message += '<p><a href="#" onclick="PolygonEditable(true);">Edit</a> '
            + '<a href="#" onclick="PolygonEditable(false);">Done</a> '
            + '<a href="#" onclick="DeleteField(myField)">Delete</a></p>'
            + coordinateMessage;

        return message;
    }

This explanation concludes our desire to build the application to requirements specified at the top of the post.