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

   
Concept: Polyline Drag Handles - Bing Maps v7

This example is based off of the Polyline Drag Handle example and is another port from v6.x to the Bing Maps AJAX Control v7. The concept and mechanics are basically the same as the original version, but now this has been updated to work with the v7 API."

In the included example, functionality has been implemented so that the Polyline on the map can be edited. Clicking on the button at the bottom of the map will place the map in “edit” mode. While in edit mode, “drag handles” will be displayed on the map that will allow the points of the Polyline to be changed. Simply drag & drop the handle to the new location and the Polyline will be updated. When dragging the handle to its new location, a “mask” of the Polyline is displayed that shows how the revised Polyline will look. Once the handle has been dropped in its new location, the original Polyline is updated. To exit “edit” mode, click on the button at the bottom the page again.

Note: With the default cursors implemented in v7, grabbing the drag handle can be a little tricky. Like the old saying goes, “If at first you don’t succeed,….”.

Walk Through:

To start with, some globally scoped variables are added to the page. These variables are used throughout the page and are as follows:

    var map = null;
    var polyline = null;
    var polylineMask = null;
    var points = new Array();
    var DragHandleLayer = new Microsoft.Maps.EntityCollection({ visible: false });
    var dragging = false;
    var pointIndex = null;
    var currentDragHandle = null;
    

“map” will house the instance of the map on the page. “polyline” will house the main Polyline on the map. “polylineMask” will house the duplicate version of the main Polyline that is used during the dragging process. “point” is an array that will house the Location objects that define the coordinates of the polyLine and polyLineMask Polylines. “DragHandleLayer” is an EntityCollection (layer) that houses the objects that are used during the dragging process (note initial state is hidden). “dragging” is the indicator used to identify if the map is in “dragging” mode. “pointIndex” is the index of the “points” array that represents the index of the point of the polyLine that is currently being moved. Lastly, “currentDragHandle” is will house a PushPin object that represents the current drag handle that is being moved.

Now that those are all in place, the map can be initialized. The map is implemented in the typical fashion, so we won’t go into those details. Next two Polylines (the main and the mask) are added to the map, as well as the drag handles. The implementation is as follows:

    //Create Polyline
    var polylineStrokeColor = new Microsoft.Maps.Color(200, 0, 0, 200);
    polyline = new Microsoft.Maps.Polyline(points, { strokeColor: polylineStrokeColor, strokeThickness: 2 });
    map.entities.push(polyline);
        
    //Create Polyline mask
    var polylineMaskStrokeColor = new Microsoft.Maps.Color(200, 100, 100, 100);
    polylineMask = new Microsoft.Maps.Polyline(points, { strokeColor: polylineMaskStrokeColor, strokeThickness: 2 });
    DragHandleLayer.push(polylineMask);
    
    //Add drag handles
    for (i = 0; i <= (points.length - 1); i++) {
        var dragHandle = new Microsoft.Maps.Pushpin(points[i], { icon: 'images/DragHandle2.gif', height: 10, width: 10, anchor: new Microsoft.Maps.Point(5, 5) });
        DragHandleLayer.push(dragHandle);
    }
    
    map.entities.push(DragHandleLayer);
    

The implementation for both Polylines is similar, but not exact. They both begin with defining the color for the polyline (lines 2 & 7) and then each respective Polyline object is created (lines 3 & 8). Lastly, both Polylines are added to an EntityCollection via the push() method, however the main Polyline, polyLine, is added to the default EntityCollection of the main map, while the second Polyline, polyLineMask, is added to the EntityCollection “DragHandleLayer”. This effectively puts the Polylines on different layers in the map. Please note that points/coordinates for each of the Polylines is defined by the “points” array. The “points” array contains a series of randomly generated Location objects (code not displayed).

Once the Polylines are implemented, the drag handles are put in place (lines 12 – 15). Each drag handle is simply a Pushpin object placed at each of the points for the previous Polylines. While looping through the “points” array, a new Pushpin is created using the Location object from the “points” array. Each Drag Handle Pushpin is defined with a custom icon, height, width and anchor. The anchor (Point object) is put in place to help move the Pushpin directly over the point of the Polyline. After each Pushpin has been created it is added to the “DragHandleLayer” EntityCollection. Finally, the “DragHandleLayer” EntityCollection is added to the main map (line 17).

Now that all of the necessary objects have been added to the map, a series of events need to be wired up to facilitate the drag handles. The implementation is as follows:

    // Attach the event handlers to the mouse
    Microsoft.Maps.Events.addHandler(map, 'mousedown', mouseDownHandler);
    Microsoft.Maps.Events.addHandler(map, 'mouseup', mouseUpHandler);
    Microsoft.Maps.Events.addHandler(map, 'mousemove', mouseMoveHandler);
    

The three events that are being captured are “mousedown”, “mouseup” and “mousemove” and these events are all relative to the map object. When these events are raised by the map, the functions “mouseDownHandler”, “mouseUpHandler” and “mouseMoveHandler”, respectively.

When the user clicks on the map, the “mousedown” event is raised and the “mouseDownHandler” function is executed. The implementation is as follows:

    //mousedown handler
    function mouseDownHandler(e) {
        if (e.targetType == 'pushpin') {
            dragging = true;
            currentDragHandle = e.target;
            
            //Determine point index
            var handleLocation = currentDragHandle.getLocation();
            for (i = 0; i <= (points.length - 1); i++) {
                var pointLocation = points[i].toString();
                if (handleLocation == pointLocation) {
                    pointIndex = i;
                    break;
                }
            }
        }
    }
    

When the function is executed an instance of a “MouseEventsArgs” object is passed to the function and assigned to the variable “e”. Since all of the drag handles are Pushpin objects, we first test to see if the object that was clicked on was a Pushpin. This is accomplished by evaluating the targetType property of the MouseEventsArg object, e (line 3). Provided that it is, the rest of the process can continue.

First the map is put into dragging mode by setting the variable “dragging” to “true”. Next the current drag handle (Pushpin object) is captured by assigning the “target” property of the MouseEventsArg object, e to the variable “currentDragHandle”. The “target” property contains the instance of the object that raised the event, in this case the Pushpin or drag handle.

Lastly, the coordinates of the drag handle are captured via the getLocation property and assigned to the variable “handleLocation”. We then need to identify the corresponding point of the polyline that needs to be updated. The “points” array contains the “location” objects that are points of the polyline, so we loop through that array and find the index of the point that corresponds to the location of the selected drag handle. This index is assigned to the variable “pointIndex” which is used later in the process.

Now that the mouse click event has been processed, as the user moves the mouse, the “mouseMoveHanadler” function will be executed. The implementation is as follows:

    //mousemove handler
    function mouseMoveHandler(e) {
        if (dragging) {
            var loc = map.tryPixelToLocation(new Microsoft.Maps.Point(e.getX(), e.getY()));
            currentDragHandle.setLocation(loc);
            points[pointIndex] = loc;
            polylineMask.setLocations(points);
            e.handled = true;
        }
    }
    

As with the previous handlers, an instance of a MouseEventsArgs object is passed to the function and assigned to the variable “e”. Since this function will be executed every time the mouse is moved, we need to ensure that the map is in the proper mode to determine if the dragging logic should be executed. The variable “dragging” is evaluated and if true, then the next set of logic is executed, otherwise the function exits.

Provided that we’re in the proper mode, first the current location of the cursor is captured (line 4). The X/Y coordinates of the e object are used to create a new Point object. That Point object is then passed to the tryPixelToLocation method in the map object so that a location object can be created. That location object represents the current location of the cursor and is assigned to the variable “loc”. The current drag handle location is then updated by passing the loc object to the setLocation method of the Pushpin, “currentDragHandle”.

Next the corresponding point of the polyline is updated. As mentioned previous, the “point” array contains the location objects that are the points of the polyline. In the mouseDownHandler function we captured the index (pointsIndex) of the point that corresponded to the drag handle. Now that the new location of the drag handle has been captured, it is used to replace the corresponding location object in the points array (line 6).

The polyline object, polylineMask, is then updated by passing the “points” array to the setLocations menthod in the polylineMask object. Lastly, the “handled” property of the of MouseEventsArg object, e, is set to true, to indicate that the event has been handled and that none of the native code for the event should be processed.

NOTE: At this point, since we are still in dragging mode, we are only updating the polyline mask and not the original polyline. The original polyline will be updated one the entire process has been completed.

This process executes each time the mouse is moved. Once the mouse button is released the last part of the process is executed, which is the mouseUpHandler. The implementation is as follows:

    //mouseup handler
    function mouseUpHandler(e) {
        if (dragging) {
            dragging = false;
            currentDragHandle = null;
            polyline.setLocations(points);
        }
    }
    

As with the other function the MouseEventsArgs object is passed to this function, however, it is not used for this process. This function essentially takes the takes the map out of dragging mode and updates the original polyline.

Again we evaluate the mode of the map and continue if in dragging mode (line 2). First the map is taken out of dragging mode by setting the “dragging” variable to false. Next the reference for the current drag handle is cleared out by setting the varialble “currentDragHandle” to null. Lastly the original polyline is updated by passing the updated “points” array to the setLocations method in the polyline object.

Now that we’ve walked through the code involved in the process of working with the drag handles and updating the polylines, the last item that remains is getting the map in and out of dragging mode. While your specific needs will vary, in this example, there is a button below the map used to toggle the map mode. When clicked, the button will execute the toggleEdit() function. The implementation is as follows:

    //toggle edit mode
    function toggleEdit() {
        DragHandleLayer.setOptions({ visible: !DragHandleLayer.getVisible() });
        document.getElementById("cmdEdit").value = (DragHandleLayer.getVisible() == false) ? 'Click to start edit' : 'Click to end edit';
    }
    

As this function is executed, it will toggle the ability to edit the polyline. The drag handles and polyline mask used in the dragging process are contained in their own layer or EntityCollection. As this function is executed it will show/hide this layer, this giving you the ability to either edit the polyline or not. Additionally, the text on the button is updated to provide a visual queue as to the current mode of the map.

Wrap-up:

Again, this example is based off of the Polyline Drag Handle example for Bing Maps v6.x. This example was updated to now work with v7 of the Bing Maps AJAX control. The basic concept remains the same, while some of the underlying processes have been updated to work with the new framework.

This is just one approach that can be taken to implement this type of functionality and is intended to illustrate the mechanics behind this concept. Your implementation will vary depending on your specific needs. 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