Creating a Web Service for RouteMapIMS

I have some projects coming up that need to do routing. And not the nice point-to-point routing either. We’re talking the infamous traveling salesman problem I talked about and wrote code for earlier. This is the kind of problem that calls out to be a web service.

There are several ways a web service or a service oriented architecture helps us. First, they’re platform and application interoperable. I can write a web service in .NET and access it with perl on a Linux box or Microsoft Word on Windows (this is particularly handy here, as I use .NET for RMIMS but I much prefer writing web sites in PHP). They allow easy code reuse. And they are based on open standards and protocols, which is what we should all strive to use.

I have a confession to make here. Even though .NET creeps me out and I’m a big proponent of open source software, I write all of my web services in .NET. .NET makes web services so easy it is almost ridiculous. If you’ve ever tried writing a WSDL file, you know what I mean. So, this web service will be in ASP.NET (VB).

Let’s take a look at a simple web service.

<%@ WebService Language=”VB” Class=”WebService” %>

Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols

<WebService(Namespace := “http://tempuri.org/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
Public Class WebService
Inherits System.Web.Services.WebService

<WebMethod()> _
Public Function HelloWorld() As String
Return “Hello World”
End Function

End Class


Basically it looks like a regular ASP.NET web form with a couple of extra goodies. The class is WebService, and some imports are probably new if you haven’t created .NET web services before. Plus some namespace (you’ll want to change the URL to something you can idetify with, although that isn’t totally necessary) and odd charaters we won’t get in to. The important bit is:

<WebMethod()> _

This tells .NET the proceeding function will be exposed as part of the service. A web service can contain as many functions as you want. Here we have just the one. It takes no arguments, and when called by a client, it returns the string “Hello World”.

If we modify the function like so:

<WebMethod()> _
Public Function HelloWorld(ByVal YourName As String) As String
Return “Hello “ & YourName
End Function


We are asking for the calling application to pass us a string (YourName). If the client passed it “Jim Bob”, it would get a return of “Hello Jim Bob”.

For this web service, the function header looks like this:

Public Function CreateRoute(ByVal strCoords As String, ByVal strStopNames As String, ByVal intHighwayPreference As Integer, ByVal strShortestOrQuickest As String, ByVal bolOptimize As Boolean, ByVal intImageWidth As Integer, ByVal intImageHeight As Integer) As String

The information the client is sending is:
strCoords: A string of coordinates for the addresses in the route.
strStopNames: A string of names to call the stops from strCoords.
intHighwayPreference: An integer (1-100) indicating how much to weigh highways.
strShortestOrQuickest: Whether to route for shortest (distance) or quickest (time)
bolOptimize: Do a traveling salesman problem (true or false)
intImageWidth and intImageHeight: The size of the image showing the route in pixels.

It takes these variables, and using RouteMapIMS, returns an XML string with all of the routing information.

<ROUTEINFO>
<ROUTE>
<DRIVE_TIME>55.8128846541201</DRIVE_TIME>
<DRIVE_DISTANCE>30.5380641828747</DRIVE_DISTANCE>
<DRIVE_TOTALS>Driving distance: 30.5 mile(s) Driving time: 56 minute(s)</DRIVE_TOTALS>
<IMG_URL>http://server URL to image </IMG_URL>
</ROUTE>
<DIRECTIONS>
<SEGMENT_NUMBER>START</SEGMENT_NUMBER>
<SEGMENT_TYPE>DEPART</SEGMENT_TYPE>
<SEGMENT_DIRECTIONS>Depart Stop One</SEGMENT_DIRECTIONS>
<SEGMENT_TIME_LENGTH />
</DIRECTIONS>
<DIRECTIONS>
<SEGMENT_NUMBER>1</SEGMENT_NUMBER>
<SEGMENT_TYPE>DIRECTION</SEGMENT_TYPE>
<SEGMENT_DIRECTIONS>Go South on Wrights Ferry Rd</SEGMENT_DIRECTIONS>
<SEGMENT_TIME_LENGTH>Drive 0.4 mile(s) ~ < 1 minute</SEGMENT_TIME_LENGTH>
</DIRECTIONS>
……..
<DIRECTIONS>
<SEGMENT_NUMBER>FINISH</SEGMENT_NUMBER>
<SEGMENT_TYPE>ARRIVE</SEGMENT_TYPE>
<SEGMENT_DIRECTIONS>Arrive at Stop Four</SEGMENT_DIRECTIONS>
<SEGMENT_TIME_LENGTH />
</DIRECTIONS>
</ROUTEINFO>


Using XML string handlers, the client can parse out any information it wants to. For example, this PHP code will return the drive time and distance totals:

<?php
$client = new SoapClient(“http://localhost/routeservice/routeservice.asmx?wsdl");

$tabarray = array(“strCoords” => “1402864 498796,1462011 481915”,
“strStopNames” => “Stop One, Stop Two”,
“intHighwayPreference” => 50,
“strShortestOrQuickest” => “quickest”,
“bolOptimize” => “FALSE”,
“intImageWidth” => 500,
“intImageHeight” => 500);


$thestr = $client->CreateRoute($tabarray);
$simpleresult = $thestr->CreateRouteResult;

$phpobject = simplexml_load_string($simpleresult);

echo $phpobject->ROUTE->DRIVE_TOTALS;
?>


I used a PHP example here for a reason. It seems that .NET will return an object via WSDL whether you tell it to return a string or not. Needless to say, this caused a lot of swearing on my part until I figured that out. To convert the return to a string so you can process it as XML you have to do $thestr->CreateRouteResult. Oh, Microsoft. Just when I was singing your praises.

As the service and config files contain a couple hundred lines of code, I’m not going to go over it all in this blog. You can download the source code here if you wan
t
to go over it in gory detail. Note for this to work for you, you’ll need RouteMapIMS and you’ll need to set up the web.config settings for your server and service. You’ll also need to put the RMIMS dll (on the install CD) in to your web service bin folder.