Maptitude GISDK Help

 

Programming in Python

Calling GISDK Functions From Python

The most common pattern is to start Maptitude, TransCAD or TransModeler, then start your python script in a separate process: a terminal window, Visual Studio Code debugger, or a Jupyter notebook. This pattern is called out-of-process: Python and Maptitude are two separate processes. With this pattern, you call GISDK functions from your Python script, using the caliperpy package.

 

About the caliperpy Package

 

The caliperpy module lets you access not only the GISDK functions, macros and classes, but also the .NET framework via any program written in Python. The caliperpy package works with scientific Python distributions such as Anaconda.

With the caliperpy package, you can write a Python program like this one

 

   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)   
     

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

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

 

Installing the Caliper Python Package

If you are using Anaconda, follow these instructions:

1. Open Anaconda Prompt via:

This will start a special command window for Anaconda. You can also enable Anaconda Python for all your command windows including Powershell, there are many web sites explaining how to do that.


2. You can use the default environment called base, or you can create a new environment of your choice (myenv here):

        conda create --name myenv

 

3. Check that the environment myenv has been created by listing all the environments:

        conda env list


4. Activate the environment of your choice:
      conda activate base
Or:
      conda activate myenv


5. Install caliperpy in your environment:
        python -m pip install "c:\Program Files\Maptitude 2024\GISDK\API\Python"

 

6. Open python in the same environment: just type python

 

7. import caliperpy in the python prompt
        python
>>import caliperpy

 

8. You can copy one of the example scripts from the program subfolder Documents\Caliper\Maptitude YYYY\CodeSamples (where YYYY is  the year of your Maptitude version) to your own Documents folder. For example, at the Windows command prompt:

 

    copy C:\Users\[Username]\Documents\Caliper\Maptitude YYYY\CodeSamples\Maptitude-example-3.py .
    python Maptitude-Example-3.py

 

Calling Python Functions From the GISDK

A script written in the GISDK programming language can call directly a Python function and fetch the result, in a similar way that the GISDK can call a .NET function written in C#.

 

The Python function runs embedded in the main hosting process (TransCAD, Maptitude or TransModeler). Your Python function can execute any GISDK function via caliperpy, and use all of the Python packages in your Python environment. This pattern is called in-process, or embedded Python. With this pattern, you call a Python function from the GISDK, and the Python function can also call the GISDK. The following sections explain how it works.

 

Register Embedded Python

To enable calling Python functions directly from the GISDK, you must indicate to Maptitude what Python distribution to use. Open your Caliper product (e.g. Maptitude), and choose: Tools > GIS Developer's Kit > Register Embedded Python.... A dialog box with a datagrid should appear. The registration process requires Windows Powershell version 7 or later to be installed on your computer.

 

The program will try its best to detect available Python distributions on your machine. You will be able to choose the distribution and click the Register button next to it to register your preferred Python distribution. You will be prompted to execute the registration steps with elevated Administrator privileges.

 

The program will install or upgrade all required Python packages, including pip, pywin32 and caliperpy, and register the selected distribution as the one to use for embedded Python. You will need to restart your product to complete the registration.

 

 

 

Write your First Embedded Python Function

After you have registered a Python distribution to embed in Maptitude, you can write an embedded Python script, or you can adapt any existing Python script to run either embedded or out-of-process using this simple three-step technique.

To get started, copy the files from the Maptitude program folder GISDK\Python\Samples\embedded-example.py and my_utils.py to a folder you can write to.

The body of the Python script must include a couple of lines in the header and a couple of lines in the footer, as follows.

  
   # first add this header before any local import statements:
    import caliperpy

    # pylint: disable=undefined-variable
    context = caliperpy.ScriptContext(PythonContext if caliperpy.IsInProcess() else None)

    # then define your functions in the body, for example:
    # local imports must be placed after the context header
    import my_utils
    def add_two_numbers(a: int, b: int) -> int:
        """ returns the sum of the input numbers """
        return my_utils.sum_two_numbers(int(a), int(b))

    # finally add this footer at the end of your script:
    if __name__ in ["__main__", "__ax_main__"]:
        context.run(locals())
     

Test Your Function in the Windows Terminal

You can now execute the function add_two_numbers in a terminal window with this command line:

  python embedded-example.py add_two_numbers a=1 b=10
  

In general, the command line syntax is:

  python script_name.py function_name arg1=val1 arg2=val2 arg3=val3 ....
  

Call Your Function From the GISDK

The GISDK now includes a standard class called “Python”. This class has been designed to execute any function in a Python script, provided the script includes the header and the footer as explained above.

In the GISDK immediate execution toolbar, execute the following two lines, with the “Trace” checkbox enabled:

 

  script = CreateObject("Python","c:/path/to/your/embedded-example.py")
  result = script.add_two_numbers({a:3,b:10})

 

First, we create a “Python” object to hold a reference to the script that contains the function or functions we would like to execute inside Maptitude. Then we execute the function as if it was a public function of this Python script object. In general, the GISDK syntax is:

 

    script = CreateObject("Python","//full/path/to/your/script_file.py")
    result = script.function_name({ arg1: val1 , arg2: val2 , arg3: val3 , ... })

 

The Maptitude runtime executes the Python function in the Maptitude main process, in a similar fashion it executes all the GISDK functions and macros and methods in a .NET class. Your Python function can return any of following types: None, string, number, bool, dictionary, tuple, list, GISDK object and np.array. A dictionary will be translated to an "option array", and an np.array is translated to a GISDK array.

 

You shoud translate your result to one of these types before returning it from your function. Many Python compound types can be translated to a dictionary (dict) or a list in one statement.

 

Your Python function can call GISDK functions via caliperpy. If your Python function requires a dataframe as input or returns a dataframe as output, you can use caliperpy functions to translate them to/from dataviews, and return the dataview name in the result. If your Python function returns a compound type that is not supported by the GISDK, the GISDK will throw an error.

 

Features in the caliperpy Package

 

Connecting to the GISDK across Python functions

The most common pattern is to use the GISDK across two or more Python functions. To simplify connecting and disconnecting from the GISDK, a new class was added, with two static methods: connect() and disconnect().

 

    import caliperpy
    class YourOwnClass(unittest.TestCase):
         def test_connecting_to_Maptitude(self):
              dk = caliperpy.Maptitude.connect()
              // use the GISDK
         def test_do_more_processing(self):
             dk = caliperpy.Maptitude.connect()
             // do some more processing
         def test_finally_disconnect_when_done(self):
             // disconnect from Maptitude when finished
             caliperpy.Maptitude.disconnect()

 

Fast Translations of GISDK Binary Tables To and From NumPy Panda DataFrames

 

Two new functions are available to translate GISDK binary files to and from numpy dataframes. The functions are very fast, and let you translate files with hundreds of thousand of rows quickly. The two functions are:


          df = dk.GetDataFrameFromBin(bin_file_name,debug_messages)
          outputFileName = dk.WriteBinFromDataFrame(df,outputFileName)
 

These functions are very fast. On a 3.6 Ghz CPU with 64 GB of RAM, translations to and from a data frame with more than 1 million rows and 9 columns of type number, it took less than 1 second. The same table with 9 columns of type string can be translated in 5 seconds.

 

Simplified RunMacro() Calling Arguments

dk.RunMacro() function now takes the same arguments as the RunMacro() function in the GISDK.

In older versions of our caliperpy package, the first argument of dk.RunMacro() should have been the "compiled UI package":

 

          dk.RunMacro("ui_database","macro name", arg1, arg2, arg3)


It is now NOT necessary to specify the "ui_database" as the first argument of dk.RunMacro()

Instead, the same arguments in the same order can be used in Python as in the GISDK:

 

          dk.RunMacro("macro name",arg1,arg2, arg3)

 

Please note that if you write your Python code by passing "compiled UI package" as the first argument, it will not work.

 

Using the caliperpy Package

In the Python command line, start by importing the package caliperpy and create a connection to the GISDK.  The folder GISDK\Samples\Python contains an example Python script that you can copy to your own Documents folder to get started.

 

    import caliperpy
    dk = caliperpy.Maptitude.connect()
    print(dk)
   

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

 

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

You can call GISDK 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.")

 

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 package "gis_ui" you write:

 

    res = dk.RunMacro("Echo","string argument",123,{ "key": "value1" , "key2": "value2" })

 

The Python syntax to call a GISDK macro is:

 

result = dk.RunMacro(macro_name,*args,**kwargs)

Argument Type Contents
macro_name string Name of the GISDK macro
args Non-keyword args List of parameters to pass to the GISDK macro
kwargs Keyword args Named arguments

 

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

 

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

This script 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:

 

  // GISDK code
  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 cannot 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 the GISDK tou can disconnect the process by calling the disconnect method:

    caliperpy.Maptitude.disconnect()
    

Only one Python script can access the GISDK at a time, and that all GISDK method calls must be executed sequentially.

 

Translating Between Numpy Dataframes and GISDK Dataviews

In the latest version of caliperpy we added Python functions to translate between numpy dataframes and GISDK dataviews.
The first three functions work with GISDK views that are open in the current workspace. The functions do not require to close the views before translating them to/from dataframes.

The three functions are: GetDataFrameFromView, GetViewFromDataFrame, and UpdateViewFromDataFrame.
The API is:

 

GetDataFrameFromView(view_set_name, field_names) -> pd.core.frame.DataFrame

 

Create a Numpy Dataframe object from an input view and set name, and a list of field names. If the list of field names is missing, it will create a dataframe with all the fields of the input view

 

    column_names = [ "ID1" ,"AB_Time" , "BA_Time" ]
    data_df = dk.GetDataFrameFromView(link_flows_table,column_names)
  

GetViewFromDataFrame(input_df,output_view_name) -> str

 

Copy the input dataframe to a new GISDK view name. Returns the name of the new view.

  data_table = dk.GetViewFromDataFrame(data_df[["ID","Distance","Speed"]],"data_table")
  num_rows = dk.GetRecordCount(data_table,None)

UpdateViewFromDataFrame(input_df, input_view_name) -> dict

 

Update columns in the existing input_view_name by copying them from the input dataframe.
This first column in the dataframe must contain unique values: it will be used to identify the rows to replace or add to the input view.
The other columns will replace existing columns or be added to the input view.
Returns a dictionary with information about rows added, rows replaced, columns added, and column replaced.

  result = dk.UpdateViewFromDataFrame(data_df,link_flows_table)
  

The following Python script demonstrates how to use the three functions:

 

""" Test updating a view from a dataframe
  """
  dk = caliperpy.Maptitude.connect()
  tutorial_folder = dk.RunMacro("G30 Tutorial Folder")
  link_flows_table = dk.OpenTable("turn_movements","ffb",[ tutorial_folder + "LinkFlows.bin",None])
  num_rows = dk.GetRecordCount(link_flows_table,None)
  print(f"table {link_flows_table} has {num_rows} rows.")
  all_column_names , _ = dk.GetFields(link_flows_table,"All")
  print(f"{link_flows_table} column names are: {all_column_names}
  # copy some of the columns to a dataframe
  column_names = [ "ID1" ,"AB_Time" , "BA_Time" ]
  data_df = dk.GetDataFrameFromView(link_flows_table,column_names)
  # Add some data to the dataframe
  max_id_value = data_df["ID1"].max()
  data_df = data_df.append({"ID1": max_id_value+1, "AB_Time": np.random.rand() ,
    "BA_Time": np.random.rand() }, ignore_index=True)
  data_df = data_df.append({"ID1": max_id_value+2, "AB_Time": np.random.rand() ,
    "BA_Time": np.random.rand() }, ignore_index=True)
  data_df["Distance"] = np.random.rand(len(data_df))
  data_df["Speed"] = np.random.rand(len(data_df)
  print("data_df shape:", data_df.shape)
  print("The first 5 rows of data_df:")
  print(data_df.head())
  # Create a new GISDK view out of some of the columns in the dataframe
  data_table = dk.GetViewFromDataFrame(data_df[["ID1","Distance","Speed"]],"data_table")
  num_rows = dk.GetRecordCount(data_table,None)
  # Add/replace rows in the original link_flows_table with all the rows from data_df
  result = dk.UpdateViewFromDataFrame(data_df,link_flows_table)
  printf(f"result: {result}")

  #result: {'destination view': 'turn_movements', 'rows added': 2,
  #         'rows replaced': 5511, 'columns added': 2, 'columns replaced': 2}

 

Finally, you can copy a dataframe to a binary table that the GISDK can open later via OpenTable. The function is this one:

 

     WriteBinFromDataFrame(data_df, output_file_name) -> str

 

Writes the input dataframe data_df to the binary table output_file_name. Returns the name of the output file.

  bin_file_name = f"{os.getcwd()}/table-name.bin"
  dk.WriteBinFromDataFrame(data_df,bin_file_name)
  table_name = dk.OpenTable("table-name","ffb",[bin_file_name,None])
  

Calling the GISDK In Jupyter Notebooks

You can use caliperpy to connect to Maptitude from Jupyter Notebooks (scripts with extension .ipynb). The program folder Python\GISDK\Samples\Python\notebooks contains an example notebook called map_tutorial.ipynb. Copy this folder to a folder you can write to. You can open the file map_tutorial.ipynb directly in Visual Studio Code, after you have installed the extensions to run jupyter notebooks, and run your notebook cell-by-cell, then save it to PDF or HTML.

 

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.add(3,4))
    print (c.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).

 

      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".  Please, refer to the section "To Compile a List of Resource Files into a Custom UI Database"  in  the Compiling and Testing with the GISDK Toolbar Help Page for details on how to compile multiple resources files into a single "ui" file.

 

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 and TransModeler, 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.htm"([ 1 , 2, 3 , 4])

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

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

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

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

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

z = dk.ArrayToVector.htm"([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})

 

    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 and TransModeler, 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.htm"([ 1 , 2, 3 , 4])
    dk.ShowArray([ "x" , x ])
    v = dk.ArrayToVector.htm"(range(1,10,1))
    print (dk.ShowArray([ "v" , v ]))
    w = dk.ArrayToVector.htm"(range(101,110,1))
    print ( dk.ShowArray([ "w" , w ]) )
    z = dk.ArrayToVector.htm"([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]]]]
 

GISDK Matrices

Maptitude provides a broad array of GISDK functions for creating and working with matrices and matrix files. Using GISDK functions you can program your own matrix creation, manipulation, and storage. A matrix file can store efficiently one or more matrices called cores of the same type (e.g. integers or doubles).

 

Like a 1-dimensional GISDK vector, a 2-dimensional GISDK matrix is stored efficiently into a single block of memory, either by row (row major) or by column (column major). In analogy to GISDK vectors, in Python you can use the double arrow operator << to express matrix assignment operations:

 

    dk = caliperpy.Maptitude.connect()
    ...
    mc = dk.GetMatrixEditorCurrency()
    v = dk.GetMatrixVector(mc, {"Marginal": "Row Sum" })
    mc << mc / v
    mc << mc + mc
    ...
    

In a matrix assignment statement the matrix core on the left hand side (mc) must exist before you can assign it.

 

The programming examples in the folder GISDK\Samples\Python demonstrate how to import and export GISDK matrices to and from dataframes and to and from the OMX file format.

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))
    

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]]
    

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" ]]:
   

The same script will run out-of-process in a Windows terminal window or in-process inside Maptitude, provided that you insert the header and the footer as explained above.

You should try your script out-of-process first, debuggging it line-by-line in your favorite IDE, e.g. Visual Studio Code with the latest Python extensions.

For example, in Visual Studio Code, open the folder containing your script (e.g. if the file is c:\Projects\embedded-example.py then open the folder C:\projects). In Visual Studio Code, choose Run – Debug – Open Configurations and add the following debug configuration to .vscode/launch.json:

 

    {
        "version": "0.2.0",
        "configurations": [
            {
                "name": "Python: Current File",
                "type": "python",
                "request": "launch",
                "program": "${file}",
                "console": "integratedTerminal",
                // args: [ "function_name" , "arg1=val1" , "arg2=val2" , ... ]
                "args": [
                    "add_two_numbers",
                    "first=1",
                    "second=10"
                ],
                "justMyCode": true
            }
        ]
    }
   

Start Maptitude on your desktop before running your python script in a terminal window or in your debugger IDE.

When a script runs in the terminal window, all print() statements are displayed in the console, and you can debug the script line-by-line.

Running Your Script Inside Maptitude

After you have run successfully the script out-of-process, you can run it in process via the GISDK. At this time, a line-by-line Python debugger is not available in-process with Maptitude.

When running in process, there is no console window, and you can view all the print() statements inside the "Debug View" desktop application by the Windows Sysinternals team, which you can download here:
https://learn.microsoft.com/en-us/sysinternals/downloads/debugview

 

Example

You can access the GISDK from your Python function via caliperpy and the familiar "dk." statements. The same statements will work out-of-process and in-process, without changes. Here is an example script with a function that reports progress inside Maptitude. You can find the script in the prorgam folder GISDK\Samples\Python\progress.py


    # Python
    from time import sleep
    import caliperpy
    # pylint: disable=undefined-variable
    context = caliperpy.ScriptContext(PythonContext if caliperpy.IsInProcess() else None

    def show_progress(first: int,second: int) -> dict:
        """ test progress using caliperpy dk """
        dk = caliperpy.Maptitude.connect()
        dk.SetErrorLogFileName(None)
        first = int(first)
        last = int(second)
        pbar = dk.CreateGISDKObject("gis_ui","G30 Progress Bar","Status", True,last–first+1)
        for n in range(first,last+1):
            message = f"the value of n is {n}"
            print(message)
            pbar.Step(1,message)
            sleep(0.4)
        return { "a": first, "b" : last ,  "difference": last - first + 1}

    if __name__ in ["__main__","__ax_main__"]:
        result = context.run(locals())

    // GISDK
    script = CreateObject("Python", Full_Path_To_Script)
    res = script.show_progress({first: 10, second: 30})
    a = res.a
    b = res.b
    diff = res.difference
 

For additional examples see also:

 

Routing.Router

Routing.Path

 

 

©2025 Caliper Corporation www.caliper.com