How to Buy

TransCAD Brochure

Travel Demand Brochure

TransModeler Traffic Simulation Software

TransCAD Transportation Planning Software

Programming TransCAD in Python 2 and Python 3

The Caliper Python module lets you access not only TransCAD, but also the .NET framework via any: program written in Python 2.7 or 3.x. The Caliper TransCAD module works well with scientific Python distributions such as Anaconda Python.

TransCAD ships with two different Python modules. If you program in Python 2.7, import caliper. If you program in Python 3.x, import caliper3.

With the Caliper module, you can write a Python program like this one (in Python 3):

import sys, traceback, caliper3
dk = caliper3.Gisdk("TransCAD")
tutorial_folder = dk.Macro("G30 Tutorial Folder")
table_name = dk.OpenTable("airports","ffb",[ tutorial_folder + 
"airports.bin",None])
num_rows = dk.GetRecordCount(table_name,None)
dk.SetView(table_name)
dk.SetSelectMax(10)
options = { 'Index Limit': 0}
query = "select * where Domestic > 10000"
num_found = dk.SelectByQuery("high traffic","several",query,options)

with the Caliper Python module, you can:

  • Execute any Gisdk function or macro via dk.FunctionName() and dk.Macro()
  • Call Gisdk classes for geocoding (Data.Finder) and multi-stop routing and reporting (Routing.Router)
  • Access any .NET assembly via dk.CreateManagedObject()
  • Access any COM object via dk.CreateCOMObject()

Note that "Gisdk" with just the "G" capitalized is always used in Python programs.

Using the Caliper Module

Once the module is installed, you are ready to use it in a Python script or in the Python command line. The folder GISDK\Samples\Python contains an example Python script that you can use to get started.

In the Python command line, start by importing the modules sys, traceback and caliper and by creating a GISDK object, which holds a live connection to one TransCAD child process:

>>> import sys, traceback, caliper

>>> dk = caliper3.Gisdk("TransCAD")

>>> print dk

<Gisdk TransCAD>
 

The GISDK object can be created with three options:

  • application_name: the name of the Caliper product COM object, defaults to "TransCAD"
  • log_file: stores all run-time exceptions and errors raised by the COM object, defaults to "python.log"
  • search_path: custom search path to use when TransCAD opens files, defaults to None

>>> dk = caliper3.Gisdk(application_name="TransCAD",

         log_file = "python.log",

         search_path = None)
 

Now you are ready to use GISDK. You can call functions using the dot notation with the dk object:

>>> folder = dk.apply("G30 Tutorial Folder","gis_ui")

>>> table_name = dk.OpenTable("airports","ffb",[folder + "airports.bin",None])

>>> num_rows   = dk.GetRecordCount(table_name,None)

>>> print "table " + table_name + " has " + str(num_rows) + " rows."

table airports has 280 rows.
 

All of the GISDK functions are available as methods of the Python object dk. The general syntax is:

>>> result = dk.FunctionName(*args)
 

You can also call macros compiled into a GISDK module with the rscc tool. For example, to execute the macro called "Echo" located in the GISDK  module "gis_ui" you write:

>>> print dk.apply("Echo","gis_ui","string argument",123,{ "key": "value1" , "key2": "value2" })

["Echo","args[1]","string argument","args[2]",123,"args[3]",[["key2","value2"],["key","value1"]]]
 

The Python syntax to call a GISDK macro is:

>>> result = dk.apply(macro_name,path_to_compiled_ui,*args,**kwargs)
 

Where:

  • macro_name: name of a GISDK macro
  • path_to_compiled_ui: GISDK compiled "ui" file that contains that macro.
  • args: list of parameters to pass to the GISDK macro
  • kwargs: collection of named parameters for the GISDK macro

You can pass Python-style named arguments (*kwargs) to the GISDK macro, and they will be translated into an GISDK option array. For example, the Python call:

>>> result = dk.apply("Geocode Macro","c:\\data\\my_macros.dbd",address="1172 Beacon St",zip_code="02461")
 

will execute the macro called "Geocode Macro" compiled in the file "c:\\data\\my_macros.dbd", passing as named input arguments address and zip_code.

The "Geocode Macro" source code (e.g. my_macros.rsc) should look like this:

macro "Geocode Macro" (kwargs)

    input_address = kwargs.address

    input_zip_code = kwargs.zip_code

    // body of the macro here

endMacro
 

Note that Python uses the keyword None instead of null. You can't leave it out as in GISDK; instead you have to write None for a null argument.

Note also that arrays and collections are two different objects in Python. Python uses square brackets for arrays:

[folder + "airports.bin",None]

Curly brackets are only used for collections, such as option arrays:

{ "key": "value1" , "key2": "value2" }

When you are done with TransCAD and you want to terminate the MAPT.exe child process, you can call the dk.Close() method:

>>> dk.Close()

Keep in mind that only one Python script can access TransCAD at the time, and that all GISDK method calls must be executed sequentially.

Programming Examples:

Here is an example showing you how to select data from a TransCAD table:

folder = dk.apply("G30 Tutorial Folder","gis_ui")
table_name = dk.OpenTable("airports","ffb",[folder + "airports.bin",None])
num_rows = dk.GetRecordCount(table_name,None)
print "table " + table_name + " has " + str(num_rows) + " rows."

dk.SetView(table_name)
dk.SetSelectMax(10)
options = { 'Index Limit': 0}
query   = "select * where Domestic > 10000"
num_found = dk.SelectByQuery("high traffic","several",query,options)
if ( num_found > 0 ) :
    print str(num_found) + " records match the query: " + query
    view_set = table_name + "|high traffic"
    field_names , field_specs = dk.GetFields(table_name,"All")
    print "field_names: " + str(field_names)
    print "field specs: " + str(field_specs)
    sort_order  = None
    options     = None
    order       = "Row"
    i = 1

for row in dk.GetRecordsValues(view_set,
                               dk.GetFirstRecord(view_set,None),
                               field_names,sort_order,num_found,order,None):
    print "[row " + str(i) + "] " + str(row)
    i = i + 1 

This prints out the following to the console:

[row 1] (u'PPG', u'PAGO PAGO INTL', u'PAGO PAGO', u'AMERICAN SAMOA', u'AS', u'US', u'51525.*A', None, None, 0, 57835, 57835, u'Western-Pacific', u'HNL', u'Estimated', 30, u'GNC 20', u'Yes', u'CS 05/23', u'NGY', u'Yes')

 

 [row 2] (u'LNY', u'LANAI', u'LANAI CITY', u'MAUI', u'HI', u'US', u'52402.*A', None, None, 0, 84150, 84150, u'Western-Pacific', u'HNL', u'Estimated', 1308, u'HAWAIIAN ISLANDS', None, u'BS 05/73', u'NGY', u'No')
 

GISDK Classes and Objects

You can access in Python custom GISDK classes and objects. For example, you can write a GISDK   program with a class called "Calculator":

// Gisdk source code

// test this Gisdk class from Python

Class "calculator" (message,args))

    init do

        self.message = message

        self.args = args

    enditem

    macro "add" (a,b) do

        Return(a+b)

    enditem

    macro "subtract" (a,b) do

        Return(a-b)

    enditem

    macro "macro with named args" (kwargs) do

        Return("address:" + kwargs.address + "zip code:" + kwargs.zip_code)

    enditem

endClass

Use the GISDK toolbox to compile this code into a GISDK "ui" module, e.g. test.dbd. In Python, you can create an instance of the "Calculator" class by creating an object of type GisdkObject:

my_options = { "key": "value1" , "key2": "value2" }

c = dk.CreateObject("calculator","c:\\path\\test.dbd",my_options)

c.message = "this message is from python"

c.args = [ 2 , 3 , 4]

print c.message

print c.args

print c.apply("add",3,4)

print c.apply("subtract",4,5)

You create an instance of a GISDK class via the CreateObject() call:

Gisdk_object = dk.CreateObject("class_name","module_name",*args,**kwargs)

You access GISDK object properties using the dot notation (e.g. c.message), and you execute object methods via the apply method: e.g.:

Gisdk_object.apply('macro name",*args,**kwargs)
 

Vectors

The Caliper module also supports working with vectors. You can create vectors using the whole set of GISDK functions, combined with the Python range shortcuts:

import sys, traceback, caliper

dk = caliper3.Gisdk("TransCAD")

folder = dk.apply("G30 Tutorial Folder","gis_ui")

view_name = dk.OpenTable("airports","ffb",[folder + "airports.bin",None])

v = dk.GetDataVector(view_name+"|","Name",None)

x = dk.VectorToArray(v)

print v

print x

 

x = dk.ArrayToVector([ 1 , 2, 3 , 4])

print dk.ShowArray([ "x" , x ])

v = dk.ArrayToVector(range(1,10,1))

print dk.ShowArray([ "v" , v ])

w = dk.ArrayToVector(range(101,110,1))

print dk.ShowArray([ "w" , w ])

z = dk.ArrayToVector([0]*10)

print dk.ShowArray([ "z" , z ])
 

You can use the GISDK assignment operator in Python "|=" also to work with simple arithmetic operations on vectors:

z |= v + w

print dk.ShowArray({ "v": v , "w": w, "z": z})
 

This prints to the console:

[["z",["vector",[0,0,0,0,0,0,0,0,0,0]]],["w",["vector",[101,102,103,104,105,106, 107,108,109]]],["v",["vector",[1,2,3,4,5,6,7,8,9]]]]

Accessing .NET Assemblies in Python

The Caliper Gisdk provides a simple method called CreateManagedObject that lets you access any .NET assembly installed on your computer. Here is a simple example for the .NET class System.DateTime:

date = dk.CreateManagedObject(None,"System.DateTime",[1980,1,1,0,0,0,0])
is_gisdk_object = dk.IsGisdkObject(date)
if is_gisdk_object:
     net_class_info = dk.GetClassInfo(date)
     print("" + str(type(net_class_info)) + " " + str(net_class_info)) later_date = date.AddDays(300.00)
     print("First date: " + date.ToString())
     print("Later date: " + later_date.ToString())

Accessing COM Objects in Python

Any COM object is also accessible in the Gisdk, via the call to CreateCOMObject(), like this:

xmldoc = dk.CreateComObject("MSXML2.DOMDocument.6.0") 
xmldoc.async = 0
xmldoc.validateOnParse = 0
print(dir(xmldoc))

More Information About Programming TransCAD in Python

If you would like more information about programming TransCAD in Python, including the Geocoding API and the Routing and Directions API, please contact Caliper Sales and we will be happy to provide you with example source code and the API documentation.

Opening a table:

The following example shows how to open a table and select some records by an SQL-like expression.


using System;
using System.Collections.Generic;
using System.Text;
using CaliperForm;
/// 
/// Open a table in the tutorial folder, and select some rows by an SQL-like expression
/// 
static void Open_Table() {
    CaliperForm.Connection Conn = new CaliperForm.Connection { MappingServer = "TransCAD" };
    Boolean opened = false;
    try {
        opened = Conn.Open();
        if (opened) {
            // You must declare dk as "dynamic" or the compiler will throw an error
            dynamic dk = Conn.Gisdk;
            string tutorial_folder = dk.Macro("G30 Tutorial Folder") as string;
            // Open a table
            string table_name = dk.OpenTable("sales", "ffb", new Object[] { tutorial_folder + "ctsales.bin", null });
            object[] fields = dk.GetFields(table_name, "All");
            var field_names = fields[0] as object[];
            var field_specs = fields[1] as object[];
            // select some records and get record values
            dk.SetView(table_name);
            int num_rows = dk.GetRecordCount(table_name, null);
            string query = "select * where Population > 200000";
            int num_found = dk.SelectByQuery("large towns", "several", query, new Object[] { new Object[] { "Index Limit", 0 } });
            if (num_found > 0) {
                string view_set = table_name + "|large towns";
                object[] sort_order = null;
                object[] options = null;
                string order = "Row";
                string first_record = dk.GetFirstRecord(view_set, null);
                Console.Out.WriteLine(query);
                Console.Out.WriteLine(string.Join(",", field_names));
                foreach (object[] row in dk.GetRecordsValues(view_set, first_record, field_names, sort_order, num_found, order, null)) {
                    string row_values = string.Join(",", row);
                    Console.Out.WriteLine(row_values);
                }
            }
            dk.CloseView(table_name);
            Console.Out.WriteLine();
            Conn.Close();
        }
    } catch (System.Exception error) {
        Console.Out.WriteLine(error.Message);
    }
}

Geocoding Street Addresses:

The following example shows how to geocode a street address.


using System;
using System.Collections.Generic;
using System.Text;
using CaliperForm;
/// 
/// Geocode a street address in the current region.
/// 
static void Geocode_Street_Address() {
    CaliperForm.Connection Conn = new CaliperForm.Connection { MappingServer = "TransCAD" };
    Boolean opened = false;
    try {
        opened = Conn.Open();
        if (opened) {
            // You must declare dk as "dynamic" or the compiler will throw an error
            dynamic dk = Conn.Gisdk;
            string tutorial_folder = dk.Macro("G30 Tutorial Folder") as string;
            // geocode an address 
            dynamic located_coord = null;
            string street_layer_db = null;
            dynamic finder = dk.CreateObject("Data.Finder");
            finder.SetRegion();
            string region_name = finder.GetRegionName();
            // Change the address to be in the current region
            var args = new OptionsArray();
            args["address"] = "200 Beacon Street";
            args["city"] = "Boston MA";
            args["silent"] = true;
            Dictionary result = Cast.ToDictionary(finder.Find("ADDRESS", args));
            // If Success:
            //      result = {"Coord": coord object, ...}
            // If Failure:
            //      result = {"Error": true, "Message": error message }
            if (result != null) {
                foreach (string key in result.Keys) {
                    Console.Out.WriteLine(key + " = " + Convert.ToString(result[key]));
                }
                var coord = result["Coord"] as dynamic;
                if (coord != null) {
                    // Success: convert output lat,lon to doubles
                    double lon = ((double)coord.Lon) / 1000000;
                    double lat = ((double)coord.Lat) / 1000000;
                    located_coord = coord;
                    street_layer_db = result["StreetLayer"] as string;
                    Console.Out.WriteLine("Address: " + result["Address"] as string + ", Location: " + lat + "," + lon);
                } else {
                    // Failure, report error message 
                    var has_errors = result["Error"];
                    var error_message = result["Message"] as string;
                    Console.Out.WriteLine("Error: " + has_errors as string + ", Message: " + error_message);
                }
            }
            Console.Out.WriteLine();
            Conn.Close();
        }
    } catch (System.Exception error) {
        Console.Out.WriteLine(error.Message);
    }
}

Geocoding a table:

The following example shows how to use a table of street addresses to locate records from a table or spreadsheet.


using System;
using System.Collections.Generic;
using System.Text;
using CaliperForm;
/// 
/// Geocode a table of street addresses and produce a geographic layer that can be added to a map.
/// 
static void Geocode_Table() {
    CaliperForm.Connection Conn = new CaliperForm.Connection { MappingServer = "TransCAD" };
    Boolean opened = false;
    try {
        opened = Conn.Open();
        if (opened) {
            // You must declare dk as "dynamic" or the compiler will throw an error
            dynamic dk = Conn.Gisdk;
            string tutorial_folder = dk.Macro("G30 Tutorial Folder") as string;
            string excel_file = tutorial_folder + "Sample Customers United States.xls";
            if (!System.IO.File.Exists(excel_file)) {
                Console.Out.WriteLine("Cannot find tutorial example file: " + excel_file);
                return;
            }
            string sheet_name = "My Customers$";
            string binary_table = tutorial_folder + "My Example Customers.bin";
            string exported_table = dk.Macro("G30 export an Excel sheet", excel_file, sheet_name, binary_table) as string;
            if (exported_table != null) {
                Console.Out.WriteLine("Exported Excel file: " + excel_file + " to: " + exported_table);
                dynamic geo = dk.CreateObject("Data.Geocoder");
                geo.SetRegion();
                string region_name = geo.GetRegionName();
                // Open the exported table
                string table_name = dk.OpenTable("customers", "ffb", new Object[] { exported_table , null });
                int num_records = dk.GetRecordCount(table_name, null);
                Console.Out.WriteLine("Locating " + Convert.ToString(num_records) + 
                                      " records in table " + table_name + " in region " + region_name + "...");
                // Get the input field specifications
                var id_field = dk.GetFieldFullSpec(table_name, "Customer ID") as string;
                var address_field = dk.GetFieldFullSpec(table_name, "Street Address") as string;
                var postal_field = dk.GetFieldFullSpec(table_name, "ZIP Code") as string;
                var city_field = dk.GetFieldFullSpec(table_name,"City") as string;
                var state_field = dk.GetFieldFullSpec(table_name, "State") as string;
                string output_layer_db = tutorial_folder + "Located Customers.dbd";
                var opts = new OptionsArray();
                opts["new_layer_name"] = "Located Customers";
                opts["out_db"] = output_layer_db;
                // First, locate records by street address and postal code, and save the result to "Located Customers.dbd"
                // input_fields = { address_field , address_field_2 , postal_code_field , city_field_or_value , state_field_or_value }
                object[] input_fields = new object[] { address_field, null, postal_field }; 
                Dictionary result = Cast.ToDictionary(geo.LocateView("ADDRESS", table_name + "|", id_field, input_fields, opts));
                // if success: 
                //    result = {"Message":"OK","NotFoundSet":"Address Not Found", 
                //              "LayerName":"Located Customers","NumRecords":4800, 
                //              "NumLocated":..., "OutputLayer":...,"GeocodingLayer":...}
                // else:
                //    result = {"Message":"Error Message","Error":1}
                if ((result != null) && (result["LayerName"] as string != null) && (result["NotFoundSet"] as string != null)) {
                    // Then, locate the records not found by postal code
                    string not_found_set = result["NotFoundSet"] as string;
                    string layer_name = result["LayerName"] as string;
                    int num_not_found = dk.GetRecordCount(layer_name, not_found_set);
                    string street_layer_db = result["GeocodingLayer"] as string;
                    Console.Out.WriteLine(Convert.ToString(num_not_found) + 
                                          " records not found by street address using street layer " + street_layer_db);
                    id_field = dk.GetFieldFullSpec(layer_name, "ID") as string;
                    postal_field = dk.GetFieldFullSpec(layer_name, "ZIP Code") as string;
                    input_fields = new object[] { postal_field };
                    result = Cast.ToDictionary(geo.LocateView("POSTAL_CODE", layer_name + "|" + not_found_set, id_field, input_fields, null));
                    var located_by_postal_code = (int) result["NumLocated"];
                    string postal_layer_db = result["GeocodingLayer"] as string;
                    Console.Out.WriteLine(Convert.ToString(located_by_postal_code) + 
                                          " records located by postal code using postal code layer " + postal_layer_db);
                }
                if (System.IO.File.Exists(output_layer_db)) {
                    Console.Out.WriteLine("Stored output geographic layer to: " + output_layer_db);
                }
            }
            Conn.Close();
        }
    } catch (System.Exception error) {
        Console.Out.WriteLine(error.Message);
    }
}

Selecting features in a circle:

The following example shows how to use select all of the point features on a map that are within a circle about a specific coordinate.


using System;
using System.Collections.Generic;
using System.Text;
using CaliperForm;
/// 
/// Select all landmark point features in a circle near a specific geographic coordinate.
/// 
static void Find_Nearest_Features() {
    CaliperForm.Connection Conn = new CaliperForm.Connection { MappingServer = "TransCAD" };
    Boolean opened = false;
    try {
        opened = Conn.Open();
        if (opened) {
            dynamic dk = Conn.Gisdk;
            // Locate by radius landmark points near located_coord
            dynamic located_coord = dk.Coord((int) (-71.071359 * 1000000.0) , (int)( 42.353844 * 1000000.0)); // Downtown Boston
            Dictionary region_prefs = Cast.ToDictionary(dk.Macro("Get Geocoding Preferences"));
            if (region_prefs == null) {
                Console.Out.WriteLine("Macro 'Get Geocoding Preferences' returned null");
                Conn.Close();
                return;
            }
            string region_file = region_prefs["region_file"] as string;
            if (region_file != null) {
                string data_directory = System.IO.Path.GetDirectoryName(region_file);
                string map_wizard_map_file = data_directory + "\\Map Wizard Maps\\MapWizardUSMap.map";
                // Open the map wizard map witht the layers for this country package
                var map_options = new OptionsArray();
                map_options["Auto Project"] = "true";
                // path used to look for layers used in the map, before other locations usually searched for
                map_options["Force Directory"] = data_directory;
                string map_name = dk.OpenMap(map_wizard_map_file, map_options);
                if (map_name == null) {
                    Console.Out.WriteLine("Cannot open map wizard map file " + map_wizard_map_file + 
                                          ". Perhaps some layers are missing?");
                    Conn.Close();
                    return;
                }
                var search_area = dk.Circle(located_coord, (double)1.0); // 3 Current Map Units (Mile, Kilometers,...)
                string layer_name = "Landmark";
                dk.SetLayer(layer_name);
                var opts = new OptionsArray(); 
                opts["Inclusion"] = "Intersecting";
                opts["Max"] = 50;
                opts["Display"] = "False";
                opts["Auto Redraw"] = "False";
                int n_found = dk.SelectByCircle("Near Coordinate", "several", search_area, opts);
                if (n_found > 0) {
                    string view_set = layer_name + "|Near Coordinate";
                    object[] fields = dk.GetFields(layer_name, "All");
                    var field_names = fields[0] as object[];
                    var field_specs = fields[1] as object[];
                    object[] sort_order = null;
                    object[] options = null;
                    string order = "Row";
                    string first_record = dk.GetFirstRecord(view_set, null);
                    string query = Convert.ToString(n_found) + " " + layer_name + " Features Near Coordinate: " + 
                                   Convert.ToString((double) located_coord.Lat / 1000000.00) + "," + 
                                   Convert.ToString((double) located_coord.Lon / 1000000.00) ;
                    Console.Out.WriteLine(query);
                    Console.Out.WriteLine(string.Join(",", field_names));
                    object[] cells;
                    foreach (object row in dk.GetRecordsValues(view_set, first_record, field_names, sort_order, n_found, order, null)) {
                        cells = row as object[];
                        if (cells != null) {
                            string row_values = string.Join(",", cells);
                            Console.Out.WriteLine(row_values);
                        }

                    }
                } else {
                    Console.Out.WriteLine("No " + layer_name + " Features Found Near Address.");
                }
            }
            Conn.Close();
        }
    } catch (System.Exception error) {
        Console.Out.WriteLine(error.Message);
    }
}

Calculating a route:

The following example shows how to use travel times to find a best route between two addresses.


using System;
using System.Collections.Generic;
using System.Text;
using CaliperForm;
/// 
/// Calculate the fastest route between 2 addresses
/// 
static void Calculate_Route()
{
    CaliperForm.Connection Conn = new CaliperForm.Connection { MappingServer = "TransCAD" };
    Boolean opened = false;
    try
    {
        opened = Conn.Open();
        if (opened)
        {
            // You must declare dk as "dynamic" or the compiler will throw an error
            dynamic dk = Conn.Gisdk;
            OptionsArray region_prefs = new OptionsArray(dk.Macro("Get Geocoding Preferences"));
            if (!region_prefs.ContainsKey("region_file")) {
                Console.Out.WriteLine("Region File missing from 'Get Geocoding Preferences'");
                Conn.Close();
                return;
            }
            string region_file = region_prefs["region_file"] as string;
            string map_wizard_map_file = System.IO.Path.GetDirectoryName(region_file) +
                                             "\\Map Wizard Maps\\MapWizardUSMap.map";
            // Open the map wizard map witht the layers for this country package
            var map_options = new OptionsArray();
            map_options["Auto Project"] = "true";
            // path used to look for layers used in the map, before other locations usually searched for
            map_options["Force Directory"] = data_directory;
            string map_name = dk.OpenMap(map_wizard_map_file, map_options);
            if (map_name == null) {
                Console.Out.WriteLine("Cannot open map wizard map file " + map_wizard_map_file 
                                       + ". Perhaps some layers are missing?");
                Conn.Close();
                return;
            }
            // geocode an address 
            dynamic finder = dk.CreateObject("Data.Finder");
            finder.SetRegion();
            var args = new OptionsArray();
            args["silent"] = true;
            args["address"] = "200 Beacon Street";
            args["city"] = "Boston MA";
            var found_origin = new OptionsArray(finder.Find("ADDRESS", args));
            args["address"] = "100 Seaport Blvd";
            var found_destination = new OptionsArray(finder.Find("ADDRESS", args));
            if (found_origin["Coord"] == null || found_destination["Coord"] == null) {
                Console.Out.WriteLine("Unable to locate origin or destination address.");
                Conn.Close();
                return;
            }

            //Calculate path using the Routing API
            var router = dk.CreateObject("Routing.Router");
            router.Minimize = "Time";
            var options = new OptionsArray();
            string xml_directions_file = dk.Macro("G30 Tutorial Folder") + "Example Directions.xml";
            options["Directions"] = xml_directions_file;
            options["DirectionsField"] = "NAME";
            options["StopDescriptions"] = new string[] { "100 Beacon St" , "200 Seaport Blvd" };
            dynamic path = router.CalculatePath(found_origin["Coord"], found_destination["Coord"],options);
            if (path == null) {
                Console.Out.WriteLine("Router error: {0}", router.ErrorMessage);
                Conn.Close();
                return;
            }

            Console.Out.WriteLine("Shortest Travel Time Path between two addresses: {0:F2} minutes.", path.Value);

            //add annotation
            var annotationOpts = new OptionsArray();
            annotationOpts["Points"] = path.Coords;
            annotationOpts["Line Color"] = dk.ColorRGB(65535, 32768, 0);
            object[] lineStyles = dk.Macro("G30 setup line styles");
            annotationOpts["Line Style"] = lineStyles[1];
            annotationOpts["Line Width"] = 8;
            dk.AddAnnotation("Map|" + map_name, "Polyline", annotationOpts);

            //calculate scope from path points
            dynamic scope = dk.GetArrayScope(path.Coords);
            //back up a little
            scope.Width = scope.Width * 1.05;
            scope.Height = scope.Height * 1.05;
            //zoom map to scope of path
            dk.SetMapScope(map_name, scope);
            dk.RedrawMap(map_name);

            // Dump the directions to the standard output
            if (System.IO.File.Exists(xml_directions_file)) {
                Console.Out.WriteLine("XML Turn by Turn Directions:");
                Console.Out.WriteLine(System.IO.File.ReadAllText(xml_directions_file));
            }
            
            Conn.Close();
        }
    }
    catch (System.Exception error)
    {
        Console.Out.WriteLine(error.Message);
    }
}