Drag-able Routes in the Bing Maps AJAX Control

Many people like the drag-able routes in the consumer facing implementation of Bing Maps. This feature basically allows you to mouse over a route-path and drag a point to a location that you want to drive past.

image

For example if I’m not happy with a route through the centre of Reading I can just  drag a point in the route-path to the M4 and dynamically re-calculate the driving directions.

image

Unfortunately there is no such feature in the API but with a little hack we can do this ourselves. The first thing that helps us here is the multi-waypoint routing in Bing Maps. You can have up to 25 waypoints, i.e. a start-point, an end-point and up to 25 points in between within a single route-calculation. When we re-calculate the route we basically set an additional waypoint.

The next important thing to know is that the VEMap.GetDirections-method returns the route-path as an array of points. So rather than using the default behaviour we can suppress the automatic drawing of the route, get the points from a callback function and draw a polyline instead. I admit that sounds weird, why would we want to do something ourselves that a build-in feature could already provide for us? Well, the answer is, that we can use the Bing Maps event system with our polyline. We will attach an event that captures when we mouse over the polyline.

function GetDirections() {
    slRoute.DeleteAllShapes();
    pointArray = new Array();

    var options = new VERouteOptions();
    options.DrawRoute = false;
    options.RouteCallback = onGotRoute;
    map.GetDirections([document.getElementById("txtStart").value, document.getElementById("txtEnd").value], options);
}

function onGotRoute(route) {
…
map.SetMapView(route.ShapePoints); var shape = new VEShape(VEShapeType.Polyline, route.ShapePoints); shape.SetLineColor(new VEColor(255, 165, 0,0.5)); shape.SetLineWidth(5); shape.HideIcon(); shape.SetTitle("MyRoute"); shape.SetZIndex(1000, 2000); slRoute.AddShape(shape); … map.AttachEvent("onmouseover", HandleMouseOverRoute); }

When this event fires we can add an icon at the location of the cursor and attach another event that captures when we move the mouse. This icon will be our drag-point and it will be visible when we mouse over the route only. When the cursor is off the route we clear this remove this drag-point again, unless we keep the left mouse-button pressed down.

function HandleMouseOverRoute(e) {
    if (e.elementID != null) {
        if (map.GetShapeByID(e.elementID).GetTitle().match("MyRoute")) {
            map.DetachEvent("onmouseover", HandleMouseOverRoute);
            var x = e.mapX;
            var y = e.mapY;
            var LL = map.PixelToLatLong(new VEPixel(x, y));
            dragPoint = new VEShape(VEShapeType.Pushpin, LL);
            dragPoint.SetCustomIcon("./IMG/drag.png");
            dragPoint.SetZIndex(1000, 2000);
            slDragPoint.AddShape(dragPoint);
            map.AttachEvent("onmousemove", HandleMouseOverRouteMove);
        }
    }
}

function HandleMouseOverRouteMove(e) {
    if (e.elementID != null) {
        if (map.GetShapeByID(e.elementID).GetTitle().match("MyRoute")) {
            var x = e.mapX;
            var y = e.mapY;
            var LL = map.PixelToLatLong(new VEPixel(x, y));
            dragPoint.SetPoints(LL);
            map.AttachEvent("onmousedown", HandleMouseDown);
        }
    }
    else {
        map.DetachEvent("onmousedown", HandleMouseDown);
        map.DetachEvent("onmousemove", HandleMouseOverRouteMove);
        slDragPoint.DeleteAllShapes();
        map.AttachEvent("onmouseover", HandleMouseOverRoute);
    }
}

When we press the mouse-button down we will start the dragging and when we release it we will end the dragging and recalculate the route

function HandleMouseDown(e) {
    map.DetachEvent("onmousemove", HandleMouseOverRouteMove);
    map.AttachEvent("onmousemove", HandleDragPointMove);
    map.AttachEvent("onmouseup", HandleMouseUp);
}

function HandleDragPointMove(e) {
    var x = e.mapX;
    var y = e.mapY;
    var LL = map.PixelToLatLong(new VEPixel(x, y));
    dragPoint.SetPoints(LL);
    return true; //prevent default behaviour
}

function HandleMouseUp(e) {
    map.DetachEvent("onmousemove", HandleDragPointMove);
    map.DetachEvent("onmouseup", HandleMouseUp);

    var x = e.mapX;
    var y = e.mapY;
    var LL = map.PixelToLatLong(new VEPixel(x, y));
    pointArray.splice(pointArray.length-1,0,LL);
    
    Reroute();
}

function Reroute() {
    slRoute.DeleteAllShapes();
    var options = new VERouteOptions();
    options.DrawRoute = false;
    options.RouteCallback = onGotRoute;
    map.GetDirections(pointArray, options);
}

Pretty simple, isn’t it? You will find a quick demo here. The complete sample code is here.

 

Advertisements
This entry was posted in Bing Maps. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s