MapInfo Pro

 View Only

MapBasic Monday: Adding Structure to your MapBasic Code

  • 1.  MapBasic Monday: Adding Structure to your MapBasic Code

    Posted 01-30-2023 04:36
    Edited by Peter Møller 02-03-2023 02:32
    Happy #MapInfoMonday and #MapBasicMonday too.

    This article starts where the introduction to MapBasic article ended. We copied MapBasic code from the MapBasic window in MapInfo Pro, compiled it, and ran the MapBasic application. Now I will show you how to add a bit of structure to your MapBasic code.

    Procedures and Functions

    I will start by introducing the concept of procedures and functions.

    Our initial application had all code in one long list of statements running from start to end. This is typically used with small scripts and tools to automate a known process. When the MapBasic application is loaded into MapInfo Pro it will execute the statement from the top and until the end.

    When writing bigger applications, you often want to add user interaction to your tool. Often you would want your tool to get loaded and place a number of controls on the ribbon interface, and then stop, and wait for the user to decide when to perform a certain action. To do so, you will have to split your code into procedures so that you can control when to execute what part of your code.

    This is what procedures and functions can do for you. They let you split your code into smaller chunks, allowing you to control when to execute what, and also allowing you to use parts of your code from multiple places. By using parts of your code from multiple places, you don't have to keep writing that code but can just execute the code stored in a procedure and function. I call this being lazy in a clever way.

    When adding procedures to your code, you have to do this in two steps for the compiler to understand it.
    1. Declare the procedure, often at the top of your source file.
    2. Create the procedure with the code you want to execute
    The declaration needs to appear before the creation. I will give you an example in the new chapter.

    The Main Procedure

    When your code has been structured into multiple procedures, MapInfo Pro needs to know where to start. This is controlled by a specific procedure The Main procedure. Make sure your code has a Main procedure as this is where MapInfo Pro will start executing the code.

    Typically you will use the Main procedure to ensure that your tool has been initialized and that it has been integrated into the user interface in the form of controls on the ribbon. You don't have to do this directly in the Main procedure. You can call other procedures from the Main procedure.

    Let's add a Main procedure to our code.

    First, we need to declare the Main procedure:
    Declare Sub Main

    Then we tell the MapBasic compiler where the procedure starts and ends using the statements Sub Main and End Sub.

    In full, it looks like this:
    Declare Sub Main
    'Sub Main: This is where the application will start when loaded
    Sub Main
    	Open Table "C:\3. Demo\2. Maps\2023\Site Selection\Customers\Customers.TAB" Interactive
    	Map From Customers
    	Set Map Window FrontWindow()  Layer 1 Display Global     Global Symbol (38,16754768,8,"MapInfo Symbols",256,0)  
    	Set Map Window FrontWindow()  Layer 1 Label Auto On
    	Set Map Window FrontWindow()  Layer 1 Label With Contract
    End Sub​

    Note that I added some comments to my code. The commenting lines start with a single quote ('). I also indent my code with a single tab inside the procedure. That makes it easier to see where the procedure starts and ends.

    Let's compile the file, and run it to see that it still works.

    Structuring your Code into Smaller Chunks

    We can also divide the existing code into smaller chunks. One part opens the table and adds it to an existing map window or creates a new map window with the table. And another part configuring the layer settings for that layer.

    To make these two additional pieces of code flexible, we will pass the name of the table to the procedures as parameters. In that way, the procedure can work with any table.

    We will even make the first piece a function instead of a procedure. This makes it possible to return the Window ID of the map window that the table was added to or created from. This Window ID can be passed onto the other procedure that configures the layer.

    This is the declaration of the function OpenTableIntoMap.

    Declare Function OpenTableIntoMap(ByVal sTabFile As String) As Integer

    You can see that it takes a single parameter called sTabFile - the table to open. A function also returns a value. In this example, the function returns an Integer value - the Window ID of the map window.

    This is how the new function looks:
    'Function OpenTableIntoMap: This function opens a table into a map window
    ' takes a name of a tab file to open
    ' returns the Window ID of the map now showing the table
    Function OpenTableIntoMap( ByVal sTabFile As String) As Integer
    	Open Table sTabFile Interactive
    	Map From PathToTableName$(sTabFile)
    	OpenTableIntoMap = FrontWindow()
    End Function​

    For now, I keep the function simple. We will add some more logic to it later to be able to determine if a map window already exists.

    I have changed the Open Table statement to use the parameters passed to the function instead of the hardcode tab file. And the Map From statement also uses the parameter. I do however need to get the table name from the file name. This can be done using the PathToTableName$ function.

    Notice the last statement before End Function: OpenTableIntoMap = FrontWindow(). That is how you specify what value to return. You set the function name equal to the value you want to return. In this case, the Window ID of the active window.

    Similarly, we can create a procedure that configures the layer settings. This procedure takes two parameters: the Window ID of the map, and the tab file.
    Declare Sub ConfigureLayer(ByVal nMID As Integer, ByVal sTabFile As String)

    'Sub ConfigureLayer: This procedure configures the layer
    ' takes the Window ID of the map with the layer
    ' takes the name of a tab file to confire
    Sub ConfigureLayer( ByVal nMID As Integer, ByVal sTabFile As String)
    	Set Map Window nMID  Layer PathToTableName$(sTabFile) Display Global     Global Symbol (38,16754768,8,"MapInfo Symbols",256,0)  
    	Set Map Window nMID  Layer PathToTableName$(sTabFile) Label Auto On
    	Set Map Window nMID  Layer PathToTableName$(sTabFile) Label With Contract
    End Sub

    I also keep this procedure simple - for now. Moving forward, we need to figure out a way to have different settings for different tables.

    I have just changed FrontWindow() to use the parameter of the Window ID, nMID, and I have changed the Layer 1 to use Layer <tablename>, where <tablename> uses the same function as earlier to get to the table name from the file name.


    We have split our code into two chunks. A function and a procedure. Now we have to use this in order to open our table.

    Before doing this, I want to introduce another concept: Variables.

    Variables allow you to capture, store and use flexible or variable - hence the name - information in your application. I have already briefly introduced you to variables through the parameters we used for our function and procedure.

    I often compared a variable to a drawer. They both allow you to store some information and retrieve and use this information again later. To start using variables, you first need to declare these, similar to how we declare our procedures and function. You declare a variable using the Dim statement:
    Dim nMID As Integer

    The statement above creates a variable called nMID of type Integer.

    The name is used to later assign a value to this variable, ie. put a value into the drawer. And it is also used when you want to use the value stored in the drawer in your script. You assign a value to the variable using the equal to operator (=):
    nMID = FrontWindow()

    And you use that value simply by inserting the variable as part of your statement, see how I used the nMID and sTabFile parameters in the ConfigureLayer procedure:
    Set Map Window nMID Layer PathToTableName$(sTabFile) Display Global Global Symbol (38,16754768,8,"MapInfo Symbols",256,0)

    The type of your variable is also important as this determines what you can save in the drawer. MapBasic has a long list of variable types, and I will not go through all here. We will talk about many of these types in coming articles but I would like to name a few:
    • Integer: Whole numbers from -2,147,483,648 to +2,147,483,647 (inclusive)
    • String: Variable-length character string, up to 32767 bytes long.
    • Object: Graphical object (Point, Region, Line, Polyline, Arc, Rectangle, Rounded Rectangle, Ellipse, Text, or Frame).
    You may also wonder why I add an "n" in front of the variable name, and even what the MID refers to as well. Long ago I learned that it was good practice to make it easy to recognize the variable type using a prefix. In this case, the "n" refers to INteger. Similarly, I use "s" for String, and "o" for Object. I have taken most of the prefixes from what is known as Hungarian Notation.

    I also tend to use abbreviations for my variable names. MID in nMID refers to map window ID. You will also see variable names like nWID referring to the ID of any window, sTab referring to a table, and sCol referring to a column (name).

    Using Procedures and Functions

    Now, we are ready to see how we can use our new procedure and function.
    'Sub Main: This is where the application will start when loaded
    Sub Main
    Dim nMID As Integer
    	nMID = OpenTableIntoMap("C:\3. Demo\2. Maps\2023\Site Selection\Customers\Customers.TAB")
    	Call ConfigureLayer(nMID, "C:\3. Demo\2. Maps\2023\Site Selection\Customers\Customers.TAB")
    End Sub

    As you can see, I have changed the Main procedure to now use our function and our procedure. I still keep it quite simple.

    First, I create a drawer, the nMID variable, to hold the Window ID of the map window created for our table.
    I then assign the return value from the OpenTableIntoMap function to the nMID variable and pass the hardcode name of our tab file to the function.
    Finally, I use the Call statement to execute the ConfigureLayer procedure. I pass the nMID variable and the hardcode name of our tab file to the procedure.

    Notice the difference between executing a function and a procedure. You execute the function by assigning its return value. A procedure is executed using the Call statement as it doesn't provide a return value.

    Compile and run the new version of our tool.

    Some final words: We have made the first steps in order to make this tool more flexible. You can in theory use this structure to open another table but you will probably find that the ConfigureLayer procedure will fail. This procedure still has some hardcoded elements, such as the name of the column for the labels. We will fix this in an upcoming article so stay tuned.

    Next time, we will start integrating controls into the ribbon interface.

    If you run into issues do not hesitate to ask questions through the comments. And also ask questions if there are things that you think I need to explain a bit more.

    Peter Horsbøll Møller
    Principal Presales Consultant | Distinguished Engineer
    Precisely | Trust in Data