One of the things I often hear from developers using Virtual Earth is "Why can't I drag stuff around on the maps?". Great question! I know some of my friends on the Virtual Earth team have posted some articles on this in the past but I wanted to show an application that Mark Merchant from the Virtual Earth team created that allows the user to drag and drop the start and end points for a map that automatically updates the driving directions between those two points. It's really easy to implement and this is all in v6.1 too.

In a nutshell the key things to this application is handling a few of the mouse events in VE and then using the Pixel To Lat Long function that takes the position of the mouse cursor and provides a new start or end point.

Virtual Earth works with a full set of events that are either map specific or standard events a user may invoke. An example of a map specific event is the onchangeview event which fires when the map is moved; standard events are onmouseup, onmousemove etc., both mouse and keyboard events are supported. In either case, VE allows you to attach new event function to any event you want via the VEMap.AttachEvent() method. This method takes two arguments, a string representing the event you want to attach to, and a user defined function which is called in lieu of the default function.

By capturing the mouse events onmousemove, onmousedown and onmouseup, then capturing the x/y position of the dropped icon and converting that into a Latitude/Longitude pair, you're able to pass those pairs into the GetDirections() function in the core.js file here below and “re-route” on the fly.

So here's the code for the html page.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Drag and Drop Driving Directions - VE!</title>
    <script type="text/javascript" src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.1"></script>
    <script src="core.js" type="text/javascript"></script>
</head>
<body onload='LoadMap();'>
    <form id="form1">
        <div id="banner" 
            style="background-color:#CCCCFF;font-family:Helvetica,sans-serif;font-style:italic;font-size:medium;width:680px;"></div>
    <div id='map' style="position:relative; width:680px; height:480px; top: 0px; left: 0px;"></div>    
        <div style="height: 77px; width: 678px;">
            <div>
                Start Address:<input id="start1" type="text"style="width: 350px" value="" /><div style="height: 84px">
                    End Address:&nbsp;<input id="end1" type="text"  style="width: 350px" value=""/><br />
                    <input id="Submit1" type="button" value="Get Directions" 
                        onclick="SetPins(document.getElementById('start1').value,document.getElementById('end1').value);"/></div>
            </div>
        </div>
    <div id="results"></div>
</form>
</body>
</html>

Next here's the code for the core.js file that is referenced in the html code above.

var myMap = null;
var pin2 = null;
var pin1 = null;
var pins = null;
var dragShape = null;
var layer = new VEShapeLayer();
//load the map to be routed on//
function LoadMap()
{
try{
    myMap=new VEMap('map');
    myMap.LoadMap();
    document.getElementById('banner').innerHTML = document.title;
    //three events calling the same method, MouseHandler//
    myMap.AttachEvent("onmousedown",MouseHandler);
    myMap.AttachEvent("onmouseup",MouseHandler);
    myMap.AttachEvent("onmousemove",MouseHandler);
    var ex = new VEException;
}
catch(ex){handleE(ex,"Main");}
}
//Set the start and end positions from user input text boxes//
function SetPins(s,e)
{
   if(s&&e !=null)
   {
   //set the pins to pass into our pin array
    pin1 = document.getElementById('start1').value;
    pin2 = document.getElementById('end1').value;
    //an array to pass into GetDirections
    pins = new Array(pin1,pin2);
    Drive();
   }
   else
   {
    alert("You need to supply a Start and End Address for this to work");    
   }
}
//the drive function that calls initializes and calls GetDirections
function Drive()
{
try{
    var options = new VERouteOptions;
    options.DrawRoute= true;
    options.SetBestMapView = true;   
    options.RouteCallback= ShowTurns;
    myMap.GetDirections(pins, options);   
    }
catch(ex){handleE(ex, "drive")}
}
function ShowTurns(route)
{
var turns = "<h3>Turn-by-Turn Directions</h3>(rounding errors are possible)";
//myMap.SetMapView(route.RouteLegs.Start, route.RouteLegs.End);
turns += "<p><b>Distance:</b> " + route.Distance.toFixed(1) + " miles";
turns += "<br/><b>Time:</b> " + GetTime(route.Time) + "</p>";
   var legs          = route.RouteLegs;
   var leg           = null;
   var turnNum       = 0;  // The turn #
   // Get intermediate legs
   try{
   for(var i = 0; i < legs.length; i++)
   {
      // Get this leg so we don't have to derefernce multiple times
      leg = legs[i];  // Leg is a VERouteLeg object
      var legNum = i + 1;
      turns += "<br/><b>Distance for leg " + legNum + ":</b> " + leg.Distance.toFixed(1) + " miles" +
               "<br/><b>Time for leg "     + legNum + ":</b> " + GetTime(leg.Time) + "<br/><br/>";
      // Unroll each intermediate leg
      var turn        = null;  // The itinerary leg
      var legDistance = null;  // The distance for this leg      
      for(var j = 0; j < leg.Itinerary.Items.length; j ++)
      {
         turnNum++;         
         turn = leg.Itinerary.Items[j];  // turn is a VERouteItineraryItem object
         turns += "<b>" + turnNum + "</b>\t" + turn.Text;
         legDistance    = turn.Distance;
         // So we don't show 0.0 for the arrival
         if(legDistance > 0)
         {
            // Round distances to 1/10ths
            turns += " (" + legDistance.toFixed(1) + " miles";
            // Append time if found
            if(turn.Time != null)
            {
               turns += "; " + GetTime(turn.Time);
            }
            turns += ")<br/>";
         } 
         turns += "<br/>";
      }
    }
  }
catch(ex){handleE(ex, "rout info");}
   // Populate DIV with directions
   SetDirections(turns);   
}
function SetDirections(s)
{
document.getElementById("results").innerHTML = s;
}
// time is an integer representing seconds
// returns a formatted string
function GetTime(time)
{
if(time == null)
{
   return("");
}
if(time > 60)
{                                 // if time == 100
   var seconds = time % 60;       // seconds == 40
   var minutes = time - seconds;  // minutes == 60
   minutes     = minutes / 60;    // minutes == 1
   if(minutes > 60)
   {                                     // if minutes == 100
      var minLeft = minutes % 60;        // minLeft    == 40
      var hours   = minutes - minLeft;   // hours      == 60
      hours       = hours / 60;          // hours      == 1
      return(hours + " hour(s), " + minLeft + " minute(s), " + seconds + " second(s)");
   }
   else
   {
      return(minutes + " minutes, " + seconds + " seconds");
   }
}
else
{
   return(time + " seconds");
}
}
function MouseHandler(e)
{
    //get the x and y position of the element being draged
    var x = e.mapX;
    var y = e.mapY;
    //pass the pixels to VEPixel in order to convert to a VELatLong
    pixel = new VEPixel(x, y);
    var LL = myMap.PixelToLatLong(pixel);
    //main body of function
    try{
   //when the left mouse button is clicked and held do this:   
    if (e.eventName == "onmousedown" && e.elementID != null)
    {
        //eliminate any other icons other than the start and finish points (there are way points)//
        if(myMap.GetShapeByID(e.elementID).GetTitle().match("Start")||myMap.GetShapeByID(e.elementID).GetTitle().match("End"))
        {        
            dragShape = myMap.GetShapeByID(e.elementID);
        }
        return true; // prevent the default action        
    }
    //When the left mouse button is released, do this//
    else if (e.eventName == "onmouseup" && myMap.GetShapeByID(e.elementID)!=null)
    {
        if(myMap.GetShapeByID(e.elementID).GetTitle().match("Start")||myMap.GetShapeByID(e.elementID).GetTitle().match("End"))
        {
            //set the lat/long from the dragShape
            LL = dragShape.GetPoints();
            var lat = LL[0].Latitude;
            var lon = LL[0].Longitude;
            /*determine if it's the start or end point being released so you can change
            the appropriate pin1 */
            if(myMap.GetShapeByID(e.elementID).GetTitle().match("Start"))
            {
               pin1 = new VELatLong(lat,lon);
            }
            else
            {
               pin2 = new VELatLong(lat,lon);
            }
            //reset the pins array with the new points
            var new_pins = new Array(pin1,pin2);
            pins = new_pins;
            //call drive, magic, new rout!
            Drive();
        }      
       dragShape = null;      
    }
    else if (e.eventName == "onmousemove" && dragShape != null)
    {
        /* continually reset the shapes' lat/long so it gets "dragged" across the map*/
        dragShape.SetPoints(LL);        
        return true; // prevent the default action
    }   
    }
    catch(ex){handleE(ex, "mouse Handler");}
    }
function handleE(ex, from)
{
    alert("Error : "+ex.message+" | occurred From Source : "+ex.source+" | By the name of : "+ex.name+" | From Method : "+from);
}

Give it a try and have fun. You can check it out here, but definitely feel free to take this little sample and play with it.

Technorati Tags: