Buy Now 

Free Trial

$695 (Or $395 Upgrade)

Videos

Reviews

Maptitude Mapping Software

About the CaliperPy Module

The CaliperPy package replaces the previously released caliperpy2 and caliperpy3 modules.  Python 2 is officially deprecated by the Python community in January 2020. Caliper has improved support for Python 3 programming, dropping support for Python 2 going forward.

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

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

import sys, caliperpy
def main():

    dk = caliperpy.Maptitude.connect()
    tutorial_folder = dk.RunMacro("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)

You do need to embed your code into a top-level function via the line:

def main():

That is required to properly dispose of objects that the Python script creates in the GISDK runtime environment when your script terminates.

In a nutshell, with the Caliper Python module, you can:

  • Execute any GISDK function or macro via dk.FunctionName() and dk.RunMacro()
  • 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 Maptitude child process:

>>> import sys, traceback, caliper
>>> dk = caliperpy.Maptitude.connect()
>>> print dk
<Gisdk Maptitude>
 

The GISDK object can be created with the log file option:

  • log_file: stores all run-time exceptions and errors raised by the COM object, defaults to "python.log"

>>> dk = caliperpy.Maptitude.connect( log_file = "python.log")
 

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

>>> folder = dk.RunMacro("G30 Tutorial Folder")
>>> 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.RunMacro("Echo","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.RunMacro(macro_name,*args,**kwargs)

Where:

  • macro_name: name of a GISDK 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:

>>> dk.SetAlternateInterface(“c:\\data\\my_macros.dbd”)
>>> dk.RunMacro("Geocode Macro",address="1172 Beacon St",zip_code="02461")
>>> dk.SetAlternateInterface(None)

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 Maptitude and you want to terminate the MAPT.exe child process, you can call the disconnect method:

>>> caliperpy.Maptitude.disconnect()

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

Logging and Debugging:

All runtime messages output by the GISDK function ShowMessage() and exceptions raised by the Maptitude child process are written to the log file python.log located in the Maptitude program folder. To write a message to the python.log file, use the GISDK function ShowMessage():

>>> dk.ShowMessage("message dumped to the python.log file")
content of python.log:
(Thu Sep 18 12:15:49 2008) message dumped to the python.log file  :  

You can print the internal values of variables passed into the Maptitude process by Python. Use the GISDK function ShowArray(), which in Python returns a string that can be printed to the console:  

>>> print (dk.ShowArray([ dk.Scope(dk.Coord(100,-100),20,30, None)]))
[["Scope","0.0001 -0.0001 20 30 Miles" ]]

To dump Maptitude variables to the python.log file, use the short-hand dk.L() which combines a call to ShowMessage() and ShowArray():

>>> dk.L([ "hello" , "world" , dk.Coord(100,-100) ])
content of python.log:
(Thu Sep 18 12:15:49 2008)  ["hello","world",["Coord", "0.0001 -0.0001" ]]:  

Compound Data Types

All of the standard GISDK compound data types are accessible in Python, using the dot notation:

>>> coord = dk.Coord(37757563, -122437162)
>>> lon   = coord.Lon
>>> lat   = coord.Lat
>>> scope = dk.Scope(coord,100,100,0)
>>> center = scope.Center
>>> w = scope.Width
>>> h = scope.Height
>>> print (dk.ShowArray({ "coord": coord , "lon": lon ,
                          "lat": lat , "scope": scope ,
                          "center": center , "w": w, "h": h }))  
[["coord",["Coord", "37.757563 -122.437162" ]],
["lon",37757563],
["lat",-122437162],
["scope",["Scope","37.757563 -122.437162 100 100 Miles" ]],
["center",["Coord", "37.757563 -122.437162" ]],
["w",100],
["h",100]]  

Tables and Views

Here is a longer example showing you how to select data from a Maptitude table:

import caliperpy, sys 
def main():
     dk = caliperpy.Maptitude.connect()
     folder = dk.RunMacro("G30 Tutorial Folder")
     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
          rh = dk.GetFirstRecord(view_set,None)
          for row in dk.GetRecordsValues(view_set,rh,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 should be able to write your entire script in Python. CaliperPy lets you call all of the GISDK functions and built-in macros in the default "gis_ui" module that ships with the program.

There are some cases when you need to write your own modules directly in the GISDK language. 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 Python3, you can create an instance of the "Calculator" class by creating an object of type GisdkObject:  

my_options = { "key": "value1" , "key2": "value2" }
compiled_ui = “c:\\path\\test.dbd
c = dk.CreateGisdkObject(compiled_ui , "calculator",my_options)
c.message = "this message is from python"
c.args = [ 2 , 3 , 4]
print (c.message)
print (c.args)
print (c.RunMacro("add",3,4))
print (c.RunMacro("subtract",4,5))  

You can create an instance of a GISDK class by calling  CreateGisdkObject():  

compiled_ui = “\\path\\to\\compiled_ui
Gisdk_object = dk.CreateGisdkObject(compiled,class_name,*args,**kwargs)  

And you execute methods via:  

Gisdk_object.MacroName(args,**kwargs)  

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

You should always dispose explicitly of the gisdk object in your Python script when you are done using it. If Gisdk_object is the name of your object, then you can either use the statement:  

Gisdk_object = None

Or you can embed your Python code within a top-level function (e.g., def main()). This will ensure that the objects created by your Python code are properly removed by from the GISDK environment when your script terminates.

If you do have an old GISDK script that does not use classes, for example:    

Macro "ProcessingMacro1" (arg1, arg2)
    result2 = RunMacro("ProcessingMacro2",arg1,arg2)
    result3 = RunMacro("ProcessingMacro3",arg3,arg4)

    // etc...
    Return(result1)
endMacro  

Macro "ProcessingMacro2" (arg3,arg4)
    Return(result2)
endMacro  

Macro "ProcessingMacro2" (arg3,arg4)
    Return(result2)
endMacro  

Macro "ProcessingMacro3" (arg3,arg4)
    Return(result2)
endMacro  

We recommend you write a GISDK class to expose only the macros that you want to call from Python.

Creating a GISDK class exposes a simple, object-oriented API to Python. You can add your own setup code to the constructor and tear down code to the destructor, and add only the public macros that you need to call from Python (ProcessingMacro1), hiding the implementation macros which do not need to be called directly in Python (ProcessingMacro2 and ProcessingMacro3).

In the example above, the Python code needs to call only the macro "Processing Macro 1". In which case, you can create a simple class:

Class "My Own Class"(args)  

// Constructor
     init do
       // Setup code
     enditem
     //  Destructor
     done do
        // Tear down code
     enditem
     macro "ProcessingMacro1" (args) do
            Return(RunMacro("ProcessingMacro1",args))
     enditem
endClass  

You can now save the GISDK class in a separate resource file, and compile it along with your old resource files into a single "GISDK UI module".  

The Python code will then look like this:  

import caliperpy
dk = caliperpy.Maptitude.connect()
gisdk_module = "c:\\ProgramData\\Caliper\\My-Own-Class.dbd"
obj = dk.CreateGisdkObject(gisdk_module,"My Own Class",args)
result = obj.ProcessingMacro1(args)
obj = None  

GISDK Vectors  

In Maptitude Vectors are a compound data type, very much like 1-dimensional arrays, except that all the elements are of the same type (e.g. string, integer or double).  

A GISDK  vector is stored efficiently into a single block of memory. In Python you can use the double arrow overloaded operator << to express vector assignment operations.

You can create vectors using the whole set of GISDK functions, combined with the Python range shortcuts:  

import sys, traceback, caliperpy
dk = caliperpy.Maptitude.connect()
folder = dk.RunMacro("G30 Tutorial Folder")
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])
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

In a vector assignment statement the vector on the left hand side (z) must exist before you can assign it.

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:

import sys, traceback, caliperpy
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.validateOnParse = 0
print(dir(xmldoc))

More Information About Programming Maptitude in Python

If you would like more information about programming Maptitude 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.