This tutorial describes the differences between a draggable and a normal route and some operation tips for using this draggable route.
Draggable route is supported since AJAX API version 2.2. It extends a normal route
by allowing users to change the course of a route interactively: A user can drag any point of the route with the mouse to another position.
You can create a draggable route by customizing a normal route object: For that you have to create a handler which takes over control for displaying and event handling of this route object. The handler is referenced as “MAP24_ROUTE_DIGITIZER”. Additionally, you must set some customer-defined properties in the handler.
While the following example gives you a recap of how to create a normal route, the second example shows how to create a draggable route.
//defines the MRC command for creating a normal route
DeclareMap24RouteObject: new Map24.WebServices.DeclareMap24RouteObject({
mrcCommands[mrcCommands.length++] = new Map24.WebServices.XMLCommandWrapper({
MapObjectID: "nmroute",
Map24RouteID: route.RouteID
})
})
//Defines the MRC command for creating a draggable route
DeclareMap24RouteObject: new Map24.WebServices.DeclareMap24RouteObject({
MapObjectID: "szroute",
Map24RouteID: route.RouteID,
Customize: new Map24.WebServices.MapObjectCustomSettings({
Handler: "MAP24_ROUTE_DIGITIZER",
// On submit a JavaScript function can be called.
Properties: [
new Map24.WebServices.Property({Key:"CALLBACK_URL",Value: "javascript:showRouteDescription({
EVENTID: '', ROUTEID: ''
})"}),
new Map24.WebServices.Property({Key:"CALLBACK_TARGET", Value:"_self"}),
new Map24.WebServices.Property({Key:"routeColor", Value:"0,0,255,100"}),
new Map24.WebServices.Property({Key:"routeDragColor", Value:"0,255,0,100"}),
new Map24.WebServices.Property({Key:"startLogoURL", Value: "route_start.png#hotspot=1,24"}),
new Map24.WebServices.Property({Key:"destLogoURL", Value: "route_dest.png#hotspot=1,24"}),
new Map24.WebServices.Property({Key:"viaLogoURL", Value: "route_via.png#hotspot=1,24"})
]
})
})
These two steps are sufficient for declaring a basic draggable route. However, this draggable route does not provide all functions for a convenient usage. In particular, it is not possible to delete an existing waypoint from the route. To be able to do that, we have to add some code that handles the mouse events that appear on the route object.
To allow for more interactivity, the mapping application should handle mouse clicks from users and provide a context menu that allows users to remove existing waypoints. The events of mouse clicks on map could be received by adding following code:
map.addListener( "Map24.Event.MapClick", mapClickHandler );
This code adds a listener which listens to map clicks from users and assigns a callback function mapClickHandler which handles the MapClick events. The function mapClickHandler filters all the mapclick events it receives and handles only those right mouse button events that happen on a route object. Since the user should be able to perform different actions, depending on the point on the route that has been clicked, we have to distinguish four different cases in the mapClickHandler function:
- Clicks on start point of the route.
- Clicks on destination point of the route.
- Clicks on one of the way points of the route.
- Clicks on the route (neither on a start, destination, or way point)
This is the code of the mapClickHandler function which handles each of the four events listed above (see the comments highlighted in bold):
function mapClickHandler(e) {
//Don't stop processing the callback stack
if( e.TopDown ) return true;
hideContextMenu();
//if the right mouse button has been clicked
if(e.Button == e.RIGHT_BUTTON){
var objStackFirst = e.ObjectStack[0];
//if the target is a route object
if(map.Session.MapObjects[objStackFirst.TargetId] instanceof Map24.Session.Route){
var waypointIndex = null;
var routeDesc = null;
var routeWaypoints = null;
var routeId = null;
var content = "";
var targetMapArray = null;
//get the route (object) id from the event
routeId = objStackFirst.TargetId;
//split the target map
//if there is just a number in the TargetMap, we will get an array containing one entry
targetMapArray = objStackFirst.TargetMap.toString().split("-");
//if a route waypoint has been clicked
if(objStackFirst.TargetType.toLowerCase() == "routewaypoint"){
//get the route description
routeDesc = map.Session.MapObjects[objStackFirst.TargetId].getDescription();
//get the route waypoints
routeWaypoints = routeDesc.Waypoints;
//if this is a route waypoint, we will have its index in the first entry of the array
waypointIndex = parseInt(targetMapArray[0]);
//Handle first event case: Mouse click happens on the start point of the route
if(waypointIndex == 0){
//if the route contains more than 2 waypoints, provide the option to remove the start point
if(routeWaypoints.length > 2){
content = "<div style='border: 0px; text-align: left; background-color: #f0f0ea'>" +
"Context Menu for <strong>start</strong> point<br/><br/>" +
"<a href=\"javascript: map.Session.MapObjects['" + routeId +
"'].removeWaypoint(0);hideContextMenu();\" target='_self'>" +
"Remove the start point</a>" +
""</div>";
}
//if the route contains less than 2 waypoints, disable the option to remove the start point
else{
content = "<div style='border: 0px; text-align: left; background-color: #f0f0ea'>" +
"Context Menu for <strong>start</strong> point<br/><br/>" +
"<span style='color:#999999;'>Remove the start point</span>" +
"</div>";
}
showContextMenu(e.Coordinate.Longitude, e.Coordinate.Latitude, content);
}
//Handle second event case: Mouse click happens on the destination point of the route
else if(waypointIndex == (routeWaypoints.length-1)){
//if the route contains more than 2 waypoints, provide the option to take out the destination point
if(routeWaypoints.length > 2){
content = "<div style='border: 0px; text-align: left; background-color: #f0f0ea'>" +
"Context Menu for <strong>destination</strong> point<br/><br/>" +
"<a href=\"javascript: map.Session.MapObjects['" + routeId + "'].removeWaypoint(" + waypointIndex +
");hideContextMenu();\" target='_self'>Remove the destination point</a>" +
"</div>";
}
//if the route contains less than 2 waypoints, disable the option to remove the destination point
else{
content = "<div style='border: 0px; text-align: left; background-color: #f0f0ea'>" +
"Context Menu for <strong>destination</strong> point<br/><br/>" +
"<span style='color:#999999;'>Remove the destination point</span>" +
"</div>";
}
showContextMenu(e.Coordinate.Longitude, e.Coordinate.Latitude, content);
}
//Handle third event case: Mouse click happens on one of the way points of the route
else{
content = "<div style='border: 0px; text-align: left; background-color: #f0f0ea'>" +
"Context Menu for Waypoint <strong>" + waypointIndex + "</strong><br/><br/>" +
"<a href=\"javascript: map.Session.MapObjects['" + routeId + "'].removeWaypoint(" + waypointIndex +
");hideContextMenu();\" target='_self'>Remove the waypoint</a>" +
"</div>";
showContextMenu(e.Coordinate.Longitude, e.Coordinate.Latitude, content);
}
}
//Handle forth event case: Mouse click happens anywhere on the route (not on a waypoint)
//and if the targetMapArray contains two entrys (previous and next waypoint)
else if(objStackFirst.TargetType == "Route" && targetMapArray.length == 2){
content = "<div style='border: 0px; text-align: left; background-color: #f0f0ea'>" +
"Context Menu for the route object<br/><br/>" +
"<a href=\"javascript: map.Session.MapObjects['" + routeId + "'].insertWaypoint(" + targetMapArray[1] + ", " +
e.Coordinate.Longitude + ", " + e.Coordinate.Latitude +
");hideContextMenu();\" target='_self'>Add a waypoint</a>" +
"</div>";
showContextMenu(e.Coordinate.Longitude, e.Coordinate.Latitude, content);
}
}
}
}
Learn how to create a context menu in the Context Menu tutorial.
For information on how to fetch and display the route description see the Routing Basics tutorial.
View example
Executable code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Draggable Route</title>
<script type="text/javascript" language="javascript" src="http://api.maptp.map24.com/ajax?appkey=Your AJAX application key goes here"></script>
<script type="text/javascript" language="javascript">
//Declare global variables
var map = null;
var remconn = null;
var LocalConn = null;
var LonValueSt = 0;
var LatValueSt = 0;
var LonValueDe = 0;
var LatValueDe = 0;
var RouteID = "route";
var StReady = false;
var DeReady = false;
var language = "en";
function goMap24(){
Map24.loadApi( ["core_api"] , map24ApiLoaded );
}
//Callback function called when the API is loaded. The map can now be shown.
function map24ApiLoaded(){
//Initialize mapping client and show map.
map = new Map24.Map();
map.addCanvas( new Map24.Canvas( { NodeName: "maparea" } ), "c" );
map.addMapClient( new Map24.MapClient.Static(), "St" );
map.show( "St", "c" );
//Handler for mouse clicks on map, it is used for adding and eliminating via points
//this listner can also be omitted, and the route is still draggable but in this case
//via points will not be able to be eliminated by mouse
map.addListener( "Map24.Event.MapClick", mapClickHandler );
}
//geocode start and destination of the route.
function showRoute(){
//Open a local connection.
LocalConn = map.Local.openConnection();
//Open a connection to the Web services.
remconn = map.WebServices.openConnection();
var start = document.getElementById("start_address").value;
var destination = document.getElementById("dest_address").value;
//submit the geocode request for start of the route.
remconn.searchFree(
new Map24.WebServices.Message.searchFreeRequest({
MapSearchFreeRequest: new Map24.WebServices.MapSearchFreeRequest({
SearchText: start,
MaxNoOfAlternatives: 1
})
}),
//callback function
this.addRouteOnSuccessSt
);
//submit the geocode request for destination of the route.
remconn.searchFree(
new Map24.WebServices.Message.searchFreeRequest({
MapSearchFreeRequest: new Map24.WebServices.MapSearchFreeRequest({
SearchText: destination,
MaxNoOfAlternatives: 1
})
}),
//callback function
this.addRouteOnSuccessDe
);
}
//Get the coordinates of the route's start point
function addRouteOnSuccessSt() {
var ResponseMapSearchFree = this.Response.getProperty("MapSearchResponse");
//use the first value
var geocodedAddress = ResponseMapSearchFree.getProperty("Alternatives")[0];
//Get longitude/latitude values
LonValueSt = geocodedAddress.getProperty("Coordinate").getProperty("Longitude");
LatValueSt = geocodedAddress.getProperty("Coordinate").getProperty("Latitude");
StReady = true;
//push the route if both start and destination of the route is geocoded.
if(StReady&&DeReady)
pushRoute();
}
//Get the coordinates of the route's destination point
function addRouteOnSuccessDe() {
var ResponseMapSearchFree = this.Response.getProperty("MapSearchResponse");
//use the first value
var geocodedAddress = ResponseMapSearchFree.getProperty("Alternatives")[0];
//Get longitude/latitude values
LonValueDe = geocodedAddress.getProperty("Coordinate").getProperty("Longitude");
LatValueDe = geocodedAddress.getProperty("Coordinate").getProperty("Latitude");
DeReady = true;
//push the route if both start and destination of the route is geocoded.
if(StReady&&DeReady)
pushRoute();
}
function pushRoute(){
StReady = false;
DeReady = false;
var remoteConn = map.WebServices.openConnection();
//callback function of this webservice connection
//this function is called if the webservice connection is opened successfully
remoteConn.onSuccess = function(){
route = this.Response.getProperty("CalculateRouteResponse").getProperty("Route");
var addDragging = "";
var mrcCommands = [];
//these are the settings for the draggable route:
addDragging = new Map24.WebServices.MapObjectCustomSettings({
Handler: "MAP24_ROUTE_DIGITIZER",
// On submit a javascript function can be called.
Properties: [
new Map24.WebServices.Property({
Key:"CALLBACK_URL",Value: "javascript:showRouteDescription({EVENTID: '', ROUTEID: ''})"
}),
new Map24.WebServices.Property({Key:"CALLBACK_TARGET", Value:"_self"}),
new Map24.WebServices.Property({Key:"routeColor", Value:"0,0,255,100"}),
new Map24.WebServices.Property({Key:"routeDragColor", Value:"0,255,0,100"}),
new Map24.WebServices.Property({Key:"startLogoURL", Value: "./route_start.png#hotspot=1,24"}),
new Map24.WebServices.Property({Key:"destLogoURL", Value: "./route_dest.png#hotspot=1,24"}),
new Map24.WebServices.Property({Key:"viaLogoURL", Value: "./route_via.png#hotspot=1,24"})
]
})
//defines the MRC command that declares the route object
mrcCommands[mrcCommands.length++] = new Map24.WebServices.XMLCommandWrapper({
DeclareMap24RouteObject: new Map24.WebServices.DeclareMap24RouteObject({
MapObjectID: "szroute",
Map24RouteID: route.RouteID,
Customize: addDragging
})
})
//defines the MRC command that sets the route object visible
mrcCommands[mrcCommands.length++] = new Map24.WebServices.XMLCommandWrapper({
ControlMapObject: new Map24.WebServices.ControlMapObject({
MapObjectIDs: ["szroute"],
Control: "ENABLE"
})
})
//defines the MRC command which resets the map view
mrcCommands[mrcCommands.length++] = new Map24.WebServices.XMLCommandWrapper({
SetMapView: new Map24.WebServices.SetMapView({
MapObjectIDs: ["szroute"],
ClippingWidth: new Map24.WebServices.SetMapViewClippingWidth({
ViewPercentage: 100
})
})
})
//submit the MRC commands by local connection
LocalConn.mapletRemoteControl(
new Map24.WebServices.Message.mapletRemoteControlRequest({
MapletRemoteControlRequest: new Map24.WebServices.MapletRemoteControlRequest({
Map24MRC: new Map24.WebServices.Map24MRC({
Commands:
mrcCommands
})
})
})
);
//calls the function to show route descriptions
showRouteDescription({"ROUTEID": route.RouteID});
};
//submit the request for calculating route
remoteConn.calculateRoute(new Map24.WebServices.Message.calculateRouteRequest({
CalculateRouteRequest: new Map24.WebServices.CalculateRouteRequest({
Start: new Map24.WebServices.CoordinateAndAddress({
Coordinate: new Map24.WebServices.Coordinate({
Longitude: LonValueSt,
Latitude: LatValueSt
})
}),
Destination: new Map24.WebServices.CoordinateAndAddress({
Coordinate: new Map24.WebServices.Coordinate({
Longitude: LonValueDe,
Latitude: LatValueDe
})
}),
IgnoreWaypoints: false,
IgnoreDescription: false
})
}));
}
//the event handler which will be triggerd by each map click
//without this event handler, it will not be possible to handle
//the via points with mouse clicks
function mapClickHandler(e) {
//don't stop processing the callback stack
if( e.TopDown ) return true;
hideContextMenu();
//if the right mouse button has been clicked
if(e.Button == e.RIGHT_BUTTON){
var objStackFirst = e.ObjectStack[0];
//if the target is a route object
if(map.Session.MapObjects[objStackFirst.TargetId] instanceof Map24.Session.Route){
var waypointIndex = null;
var routeDesc = null;
var routeWaypoints = null;
var routeId = null;
var content = "";
var targetMapArray = null;
//get the route (object) id from the event
routeId = objStackFirst.TargetId;
//split the target map
//if there is just a number in the TargetMap, we will get an array containing one entry
targetMapArray = objStackFirst.TargetMap.toString().split("-");
//if a route waypoint has been clicked
if(objStackFirst.TargetType.toLowerCase() == "routewaypoint"){
//get the route description
routeDesc = map.Session.MapObjects[objStackFirst.TargetId].getDescription();
//get the route waypoints
routeWaypoints = routeDesc.Waypoints;
//if this is a route waypoint, we will have its index in the first entry of the array
waypointIndex = parseInt(targetMapArray[0]);
//if the clicked object is the start point of the route
if(waypointIndex == 0){
//if the route contains more than 2 waypoints, we could let the user taking out the start point
if(routeWaypoints.length > 2){
content = "<div style='border: 0px; text-align: left; background-color: #f0f0ea'>" +
"Context Menu for <strong>start</strong> point<br/><br/>" +
"<a href=\"javascript: map.Session.MapObjects['" + routeId + "'].removeWaypoint(0);hideContextMenu();\" target='_self'>Remove the start point</a>" +
"</div>";
}
//otherwise we do not allowed the user to take out the start point
else{
content = "<div style='border: 0px; text-align: left; background-color: #f0f0ea'>" +
"Context Menu for <strong>start</strong> point<br/><br/>" +
"<span style='color:#999999;'>Remove the start point</span>" +
"</div>";
}
showContextMenu(e.Coordinate.Longitude, e.Coordinate.Latitude, content);
}
//if the clicked object is the destination point of the route
else if(waypointIndex == (routeWaypoints.length-1)){
//if the route contains more than 2 waypoints, we could let the user taking out the destination point
if(routeWaypoints.length > 2){
content = "<div style='border: 0px; text-align: left; background-color: #f0f0ea'>" +
"Context Menu for <strong>destination</strong> point<br/><br/>" +
"<a href=\"javascript: map.Session.MapObjects['" + routeId + "'].removeWaypoint(" + waypointIndex + ");hideContextMenu();\" target='_self'>Remove the destination point</a>" +
"</div>";
}
//otherwise we do not allowed the user to take out the destination point
else{
content = "<div style='border: 0px; text-align: left; background-color: #f0f0ea'>" +
"Context Menu for <strong>destination</strong> point<br/><br/>" +
"<span style='color:#999999;'>Remove the destination point</span>" +
"</div>";
}
showContextMenu(e.Coordinate.Longitude, e.Coordinate.Latitude, content);
}
//if the clicked object is an other waypoint of the route
else{
content = "<div style='border: 0px; text-align: left; background-color: #f0f0ea'>" +
"Context Menu for Waypoint <strong>" + waypointIndex + "</strong><br/><br/>" +
"<a href=\"javascript: map.Session.MapObjects['" + routeId + "'].removeWaypoint(" + waypointIndex + ");hideContextMenu();\" target='_self'>Remove the waypoint</a>" +
"</div>";
showContextMenu(e.Coordinate.Longitude, e.Coordinate.Latitude, content);
}
}
//if the click happened on anywhere on the route (not on a waypoint).
//and if the targetMapArray contains two entrys (previous and next waypoint)
else if(objStackFirst.TargetType == "Route" && targetMapArray.length == 2){
content = "<div style='border: 0px; text-align: left; background-color: #f0f0ea'>" +
"Context Menu for the route object<br/><br/>" +
"<a href=\"javascript: map.Session.MapObjects['" + routeId + "'].insertWaypoint(" + targetMapArray[1] + ", " + e.Coordinate.Longitude + ", " + e.Coordinate.Latitude + ");hideContextMenu();\" target='_self'>Add a waypoint</a>" +
"</div>";
showContextMenu(e.Coordinate.Longitude, e.Coordinate.Latitude, content);
}
}
}
}
//hide the context menu
function hideContextMenu() {
//if the context menu isn't visible, don't hide it
if(contextMenuVisibility == false){
return;
}
var commands = [
new Map24.WebServices.XMLCommandWrapper({
ControlMapObject: new Map24.WebServices.ControlMapObject({
Control: "DISABLE",
MapObjectIDs: [
"_ctxMenu"
]
})
})
];
LocalConn.mapletRemoteControl(new Map24.WebServices.Message.mapletRemoteControlRequest({
MapletRemoteControlRequest: new Map24.WebServices.MapletRemoteControlRequest({
Map24MRC: new Map24.WebServices.Map24MRC({
Commands: commands
})
})
}));
contextMenuVisibility = false;
}
//retrieve a calculated Route from the Routing service
//and display on web page if the route is a final result
function showRouteDescription(args) {
if (args['ROUTEID'] && RouteID != args['ROUTEID'])
//this is the original or final route
RouteID = args['ROUTEID'];
else
//one route description is displayed on web page and language is selected
if (args['language'] && RouteID != "route")
language = args['language'];
else
return;
var remoteConn = map.WebServices.openConnection();
//the request retrieves previously calculated Route from the route service
remoteConn.fetchRoute( new Map24.WebServices.Message.fetchRouteRequest({
FetchRouteRequest: new Map24.WebServices.FetchRouteRequest({
RouteID: RouteID,
IgnoreWaypoints: false,
IgnoreDescription: false,
DescriptionLanguage: language
})
}))
//if the connection is opened successfully, start
//this function to extract route description from FetchRouteResponse
remoteConn.onSuccess = function(){
var route = this.Response.getProperty("FetchRouteResponse").getProperty("Route");
route.update(true);
//we store the route descriptions in this variable route_table
var route_table = [];
route_table[route_table.length++] = '<ul style="padding-left: 20px;list-style-position:outside;list-style-type:decimal;">';
for(var i = 0; i < route.Segments.length; i++) {
route_table[route_table.length++] = '<li style="padding-bottom:3px;padding-top:3px;border-top:1px solid #D4D4D4;">';
//replace map24 tags
var formatted_text = route.Segments[i].Descriptions[0].Text;
formatted_text = formatted_text.replace(/\[M24_STREET\]/g, '<b>' );
formatted_text = formatted_text.replace(/\[\/M24_STREET\]/g, '</b>' );
formatted_text = formatted_text.replace(/\[M24_LENGTH\]/g, '<b style="white-space:nowrap;">' );
formatted_text = formatted_text.replace(/\[\/M24_LENGTH\]/g, '</b>' );
formatted_text = formatted_text.replace(/(\[|\[\/)[0-9A-Z_]+\]/g, '' );
route_table[route_table.length++] = formatted_text;
route_table[route_table.length++] = '</li>';
}
route_table[route_table.length++] = '</ul>';
route_table = route_table.join("");
document.getElementById("routeDescriptionText").innerHTML = route_table;
}
}
</script>
</head>
<body onload="goMap24();">
<div id="maparea" style="width:700px;height:500px;background-color:#E0E0E0;"></div>
<br />
<input type='text' name='start_address' id='start_address' size='25' value='Street, City, ...' /><br/>
<input type='text' name='dest_address' id='dest_address' size='25' value='Street, City, ...' />
<input type="button" value="Route" onclick="showRoute();" />
<img src="./flag_gb.gif" alt="English" style="cursor: pointer;" onclick="showRouteDescription({language:'en'});" />
<img src="./flag_de.gif" alt="German" style="cursor: pointer;" onclick="showRouteDescription({language:'de'});" />
<img src="./flag_es.gif" alt="Spanish" style="cursor: pointer;" onclick="showRouteDescription({language:'es'});" />
<img src="./flag_fr.gif" alt="French" style="cursor: pointer;" onclick="showRouteDescription({language:'fr'});" />
<br />
<table>
<tr style="margin-top: 5px;">
<td colspan="2" id="routeDescriptionText">
...
</td>
</tr>
</table>
</body>
</html>