Bing Maps Concepts
(formerly Virtual Earth)
Mike Garza   
VEMaps@garzilla.net   

   
Concept: Polyline Drag Handles

In this example we will look at one way to implement “drag handles” for editing a poly line. You may come across a situation when an existing poly line needs to be modified. Implementing drag handles to adjust the points on the poly line is an effective means of editing as it allows the end user to interact directly with the poly line on the map. In the absence of such functionality, the end user would need to either re-create the poly line or manually key in new values for a given point.

In the example to the right, functionality has been added to edit the existing poly line on the map. Please note that the poly line has been created using random points within the map, so if it looks odd, just refresh the page. At the bottom of the map there is button to begin the editing process.

When the button is clicked, drag handles will appear at each point in the poly line. When you click on a given drag handle you can move it to a new location on the map and drop it. When the dragging begins an additional poly line (mask) is displayed to illustrate how the revised poly line will look. Once the drag handle is dropped, the mask is hidden and the original poly line is updated with the new point.

Walk Through:

Admittedly, my articles up to this point have been a little long. I tend to ramble on about topics that you are probably already familiar with. Going forward, I will try to be more brief and to the point, but, knowing me (like I do), I will probably still ramble on. I apologize in advance.

Before we create the map, some global variables need to be declared. Making these variable global puts them in scope for any JavaScript function on the page. These variables are as follows:
    var map = null;
    var polyline = null;
    var polylineMask = null;
    var points = new Array();
    var moving = false;
    var currentDragHandle = null;
    var pinLayer = null;
    var pointIndex = null;
    
“Map” will be used for the instance of the map (VEMap) on the page. “polyline” will be used for the instance of the poly line (VESHape) to be edited. “polylineMask” will be used for the instance of the poly line mask (VEShape) that will show the new version of the poly line. “points” is an array that will be used to persist all of the points for the poly line(s). “moving” is a Boolean value used to indicate if a drag handle is being moved. “currentDrangHandle” will be used for the instance of the drag handle (VEShape) that is being moved. “pinLayer” will be used for the instance of the shape layer (VEShapeLayer) that will house all of the drag handles. Lastly, “pointIndex” will be used to refer to the index in the “points” array that corresponds to the drag handle that is being moved.

As you would expect, the map is created in the typical fashion, so we won’t go into that as by now we are all experts with that implementation. And as you would expect, like most of the examples on this site, the mouse is involved so we are going to have to wire up some events. I know you are as excited as me, so let’s move on.

Three events are used in this example and they all pertain to the mouse. The implementation is as follows:
    // Attach the event handlers to the mouse 
    map.AttachEvent("onmousemove", mouseMoveHandler);
    map.AttachEvent("onmousedown", mouseDownHandler);
    map.AttachEvent("onmouseup", mouseUpHandler);
    
These events are registered via the AttachEvent() method on the VEMap object, map. The events being captured are “onmousemove”, “onmousedown” and “onmouseup”. These events are wired to “mouseMoveHandler”, “mouseDownHandler” and “mouseUpHandler”, respectively.

One thing to note is that this example partially deals with moving push pins, which in this case are the drag handles. The articles on this site about Moveable Push Pins would serve as a good reference for more detail on the process that may not be covered here.

Now that the map has been established and wired, the main elements of the map can be added. These elements are the poly line to be edited, the poly line mask and the drag handles. The code that is used to create the poly line to be edited is somewhat irrelevant. Three random points within the map are gathered and used as the points for the main poly line. All of these points are places inside of the array “points” and used to create the main poly line. Additionally, this array of points is also used to create the poly line mask. The creation of the poly lines is pretty basic. You can refer to the source code on this page for more information.

Now that the poly lines have been established, we can add the drag handles to the map. The implementation is as follows:
    //Add drag handles
    pinLayer = new VEShapeLayer();
    map.AddShapeLayer(pinLayer);
    for (i = 0; i <= (points.length -1); i++){
        var dragHandle = new VEShape(VEShapeType.Pushpin, points[i]);
        dragHandle.SetCustomIcon('images/DragHandle.gif');
        pinLayer.AddShape(dragHandle);
    }
    pinLayer.Hide();
    
First a new shape layer (VESHapeLayer) is created to hold the drag handles (VESHape) and the shape layer is added to the map via the AddShapeLayer method on the VEMap object, map. Since we want the drag handles to match the existing points on the poly line, we loop through the array “points” and create a new VEShape for each point in the array of VELatLong objects. Each VEShape object is added to the shape layer “pinLayer” which was previously added to the map. Lastly, the pinLayer is set to hidden, via the Hide method, as we are not ready to use it just yet.

Now that the basic map has been established, we can focus on the event handlers. As mentioned there are three events that are in play for this example. They are as follows.

mouseDownHandler – this function will handle the onmousedown event. Its primary function it to begin the dragging process if a drag handle was clicked on. The implementation is as follows:
    //onmousedown handler
    function mouseDownHandler(e) {
        if (e.elementID) {
            currentDragHandle = map.GetShapeByID(e.elementID);
            if (currentDragHandle.GetType() == VEShapeType.Pushpin){
               moving = true;
               polylineMask.Show();
               map.vemapcontrol.EnableGeoCommunity(true);
               document.getElementById("MapDiv").style.cursor = 'crosshair';
               
               //Determine point index
               var handleLocation = currentDragHandle.GetPoints();
               for (i = 0; i <= (points.length - 1); i++){
                var pointLocation = points[i].toString();
                if(handleLocation == pointLocation){
                    pointIndex = i;
                    break;
                }   
               }       
            }
        }
    }
    
First we want to make sure that we have a valid drag handle to start the moving process. The two if statements at the beginning of the code facilitate this. The first test ensures that we have a valid VE object associated with the event and the second test ensures that the object is a push pin (drag handle).

Once we know we have the right type of object, we can start the move process. First we put the map in move mode by setting the value of “moving” to true. Next we display the mask for the poly line. We need to mouse to interact with the map for dragging, so EnableGeoCommunity() is set to true to allow us to do that. Lastly we updated the mouse cursor to a “crosshair” for a visual representation of this mode.

Since we are now in moving mode, we need to determine which point in the poly line corresponds to the drag handle. First we retrieve the current points from the drag handle and assign that value to “handleLocation”. Next we loop through the “points” array, which are all of the point for the poly line, and identify the point that matches the value of the drag handle (in handleLocation). Once we find a match, the index of that element in the array is assigned to the varialble “pointIndex”. Now the corresponding point in the array has been identified.

mouseMoveHandler – this function will handle the onmousemove event. Its primary function is to reposition the drag handle and poly line mask to the new location of the cursor. The implementation is as follows:
    //onmousemove handler
    function mouseMoveHandler(e) {
        if (moving) {
            var loc = map.PixelToLatLong(new VEPixel(e.mapX, e.mapY));
            currentDragHandle.SetPoints(loc);
            points[pointIndex] = loc;
            polylineMask.SetPoints(points);
        }
    }
    
Since this function will execute each time the mouse moves, we want to make sure that we are in “moving” mode. We test the value of the variable “moving” and if “true” we continue. Next we capture the location of the cursor, from the event object e, and create a VELatLong object that is assigned to the variable “loc”. The loc variable is used to update the location of the drag handle, via it’s SetPoints() method. The corresponding point in the “points” array is also updated with the cursor location (loc). As you recall the variable “pointIndex” identifies the corresponding point in the “points” array for the drag handle. Lastly, the poly line mask is redrawn by passing the updates points array to the SetPoints() method on the poly line mask (VEShape).

This process repositions the drag handle and poly line mask and is repeated each time the mouse moves.

mouseUpHandler – this function will handle the onmouseup event. Its primary function is to take us out of moving mode and restore map to its “pre-move” state. The implementation is as follows:
    //onmouseup handler
    function mouseUpHandler(e) {
        if (moving) {
            moving = false;
            polylineMask.Hide();
            map.vemapcontrol.EnableGeoCommunity(false);
            document.getElementById("MapDiv").style.cursor = '';
            polyline.SetPoints(points);
        }
    }
    
The undo process should only occur if the map was previously in moving mode. The variable moving is tested to determine this and if we are in moving mode, the remainder of the code is executed. The remainder of the code essentially undoes what the onmousedown event did. That being the case, “moving” is set to “false”, the poly line mask is hidden, EnableGeoCommunity is set to false and the mouse cursor is restored.

One part that is not an “undo” step is the last line of code. This code updates the original poly line with the new points that were established during the move process. The points array is passed to the SetPoints() method in the original poly line. Now the original poly line has been updated.

The last piece of code that needs to be touched on is the code that put us into “edit” mode. There is button at the bottom of the map that when clicked, will toggle the edit mode. The implementation is as follows:
    //toggle edit mode
    function toggleEdit(){
        if (!pinLayer.GetVisibility()){
            pinLayer.Show();
            document.getElementById("cmdEdit").value = 'Click to end edit';
        } else {
            pinLayer.Hide();
            document.getElementById("cmdEdit").value = 'Click to start edit';
        }
    }    
    
The pinLayer (VEShapeLayer) object contains all of the drag handles. Its visibility can determine if we are in edit mode or not. The visibility of the shape layer is evaluated by calling the GetVisibility() methods which returns a Boolean value that indicate whether or not the layer is visible.

If pinLayer is not visible, we display it via it’s Show() method and update the text on the button. This enables the edit mode. If pinLayer is visible, then we hide it via it’s Hide() method and set the button text to it’s original value.

Wrap-up:

Wow…so much for a short article. The funny part is that I think this is not as descriptive as usual, so I can only imagine how long this could have been!

Anyway, the previous was an example of how drag handles could be implemented to aide in the process of editing a poly line. As always, this is just one approach that could be taken. There are others that may be better suited to your particular needs. Hopefully, this has shed some light on how this could be accomplished.

As always, feedback, comments or questions are welcome. I look forward to hearing from you.

Return to Home Page

©2007-2017, Mike Garza, All Rights Reserved