Title of Paper

 

Migrating Avenue to ArcGIS™

 

Authors' Names

 

Constantine N. Tonias, P.E. and Elias C. Tonias, P.E.

 

Abstract

 

With the increased use and popularity of ArcGIS™, the need to migrate Avenue based applications to the ArcGIS™ environment has increased significantly.  As such, many developers are faced with the dilemma of how to efficiently convert their Avenue code into a format that utilizes ArcObjects, and which is compatible with ArcGIS™.  One approach, which this paper addresses, is to develop a series of “wraparounds” that facilitates the conversion process.  That is, the creation of a library of procedures that emulate the function of Avenue requests.  By establishing a one to one correspondence between Avenue requests and “wraparounds”, the developer is able to substitute an Avenue request with the appropriate “wraparound”, thereby significantly reducing the amount of time required to perform the conversion.

 

This paper is a case study of how thousands of lines of Avenue code were converted into VB/VBA using a “wraparound” approach.  Topics covered in this paper include: general Avenue to VB/VBA syntax differences, Views, Themes, Tables, Selection Sets, Graphic Elements, Querying, Calculating, File I/O operations, Message Boxes, Progress Bars, User-Document interaction, Manipulation of Feature Shapes, Legends, Classifications and Application deployment.

 

Paper Body

 

1. Introduction

 

With the increased use and popularity of ArcGIS™, the need to migrate Avenue based applications to the ArcGIS environment has increased significantly.  As such, many developers are faced with the dilemma of how to efficiently convert their Avenue code into a format that utilizes ArcObjects™, and which is compatible with ArcGIS.  One approach, which this paper addresses, is to develop a series of “wraparounds” that facilitates the conversion process.  That is, the creation of a library of procedures that emulate the function of Avenue requests.  By establishing a one to one correspondence between Avenue requests and “wraparounds”, the developer is able to substitute an Avenue request with the appropriate “wraparound”, thereby significantly reducing the amount of time required to perform the conversion.

 

Like most new explorations, it is the pioneers that catch the first arrows.  Having gone through the process of converting thousands of lines of Avenue code, the authors have learned, and wish to share a few lessons, which might benefit their fellow Avenue programmers who will be converting their applications to the ArcGIS environment.  After all, we don't all have to be pioneers.

 

During the mid to late 1990's, The CEDRA Corporation had developed a number of Avenue based applications for shapefile editing (CEDRA-AVcad™), coordinate geometry computations (CEDRA-AVcogo™), parcel mapping (CEDRA-AVparcel™), sewer modeling (CEDRA-AVsand™) and water distribution modeling (CEDRA-AVwater™).  All of these applications represented well over 500,000 lines of Avenue code.  As CEDRA began the conversion to the ArcGIS environment, a systematic approach was taken to develop a "wraparound" for each of the Avenue requests, which was used in their software.  As more and more of the software was converted, the number of "wraparounds" increased, and the conversion began to pick up speed.  CEDRA felt that since the use of these "wraparounds" greatly enhanced its conversion process, than surely others could benefit as well.  With this in mind, CEDRA has published a book titled Avenue Wraps, containing all of the "wraparounds" created during its conversion process in digital (optional CD), as well as, descriptive form.

 

Just as with the 90-10 software rule, that is, a user only uses 10% of a software's total functionality, Avenue developers probably only use 10% of the available Avenue requests.  Although an accurate figure is not available, it is approximated that at least 2500 to 3000 Avenue requests exist.  Avenue Wraps contains over 280 "wraparounds", which is approximately 10% of the total number of Avenue requests.  The "wraparounds" represent a one-to-one mapping for the most popular Avenue requests.  In addition to the Avenue requests used in the CEDRA software, all of the requests identified in the Introduction to Avenue™ training book have a corresponding Avenue Wrap.

 

Presented below, in the sequence of the Chapters in the Avenue Wraps book, is a description of what was involved in converting CEDRA's Avenue based applications to ArcGIS.  The intent of this presentation is to (a) demonstrate how Avenue code can be converted to ArcGIS code, and (b) describe the methodology behind the conversion.

 

2. General Conversion Guidelines

 

In migrating to the ArcGIS environment, the developer actually has a choice in how to integrate with ArcGIS.  Since ArcGIS is a Component Object Model (COM) based application, a developer can use any COM compliant programming language to interface with ArcGIS.  This includes Visual Basic, Visual C++ and the like.  If one wishes to minimize the amount of recoding, the clear choice is to perform the migration in the Visual Basic (VB) and Visual Basic for Applications (VBA) environments.  The reason being is that Avenue syntax is similar to VB/VBA.  So that, VB/VBA makes perfect sense, if one does not wish to perform a complete rewrite.  As a matter of fact, Avenue was developed using VB as the basis for the language.  Note that there is a difference between VB and VBA.  This difference will be addressed later on.

 

Between VB and VBA, the VBA environment is most similar to the Avenue environment.  Specifically, the programmer can easily develop, and test the application directly within ArcMap.  Any other COM programming language would involve the developer to (a) develop the code, (b) compile the code, (c) create an Active X DLL, (d) load the Active X DLL in ArcMap, and (e) test the code.  If an error were detected, the developer would have to exit ArcMap, correct the error, and repeat the process described above.  This approach leads to a much longer conversion process.  Therefore, it makes senses to convert Avenue code in the VBA environment.

 

In converting Avenue code there are primarily three aspects, which the programmer needs to address.

 

First, there is the issue of the syntax differences between Avenue and VB/VBA.  This includes:

         Differences in how statements are structured (if ... then ... end, for each ... end, etc.),

         Differences in names of intrinsic functions such as square root (sqrt vs. sqr),

         Manipulation of string variables (Count vs. len),

         Handling of lists (Get vs. Item),

and so forth.  These, however, are relatively easy to solve, and do not provide much of a problem, other than time.

 

Second, unlike Avenue in which variables did not have to be declared, in the VB/VBA environment variables do have to be declared.  This issue, although not terribly difficult, is time consuming and tedious, and if not done right, can cause errors, which are difficult to debug.  For those who have developed programs in Fortran, C, C++ and the like know, variable declaration is not a glamorous aspect of programming, and is a stark departure from Avenue, in which variables could be created without regard to specification of type.  So that, like the item discussed above, this part of the conversion is relatively easy to overcome.

 

Third, there is the issue of writing the appropriate ArcObjects code to perform the task of the Avenue request that is to be converted.  This is where most of the conversion work will be spent, and where the “wraparounds” come into play.  By using “wraparounds”, the developer no longer needs to worry about writing the appropriate ArcObjects code.  This code is buried within the “wraparound”.  Using Avenue Wraps, the developer searches its library of procedures for the “wraparound” which corresponds to the desired Avenue request.  For Avenue programmers, who have no experience with ArcObjects, trying to convert Avenue code using ArcObjects is going to be a difficult task.  That is why this notion of "wraparounds" is so important and essential to the conversion process.

 

As an example of how “wraparounds” can be used, let’s review the Avenue code shown below:

 

              Msg = "Okay to continue ?"

              Heading = "My Command"

              Default = True

              ians = MsgBox.YesNoCancel(Msg, Heading, Default)

              if (ians = Nil) then

                 .... do something

              end

              if (ians.Not) then

                 .... do something

              else

                 .... do something

              end

 

The Avenue code demonstrates the use of the YesNoCancel request as applied to the MsgBox class along with how the response from the user is handled.  Below is how the Avenue code could be converted in VB/VBA using Avenue Wraps:

 

              Dim Msg As Variant, Heading As Variant

              Dim Default As Boolean

              Dim ians As Integer

              Msg = "Okay to continue ?"

              Heading = "My Command"

              Default = True

              Call avMsgBoxYesNoCancel(Msg, Heading, Default, ians)

              If (ians = vbCancel) Then

                 .... do something

              End If

              If (ians = vbNo) Then

                 .... do something

              End If

              If (ians = vbYes) Then

                 .... do something

              End If

 

Note the use of the “wraparound”, avMsgBoxYesNoCancel.  In addition to the use of the “wraparound”, note:

         That the variables have been declared using the Dim statement, and that in the first declaration statement where two variables appear, the type of variable assignment is applied to each variable (not just once at the end of the declaration),

         The syntax difference in the use of the “if ... then” statement (there must be an End If, and not just an End), and

         The variable “ians” that is passed back by the YesNoCancel request is a Boolean, while the avMsgBoxYesNoCancel “wraparound” returns the same variable as an integer value, which is equal to one of the predefined VB constants.

 

“If..Then” statements:            Regarding the use of the “If..Then” statement, although the overall structure of this statement is similar in both Avenue and VB/VBA, there are some minute differences that should be brought to light.  While VB/VBA does an excellent job in displaying syntax errors, it will not flag the “end” statement in the Avenue code sample as an error.  As shown in the VB/VBA code, the “End If” terminates the “if..then” statement, and not the statement “end” statement by itself.  Thus the programmer must be cognizant of this during the conversion.

 

Furthermore, in Avenue one may write:

 

              If (a.NE.B) then x = y End

 

While in VB/VBA, the programmer would write:

 

              If (a <> b) then

                 x = y

              End If

 

Thus, perhaps one of the first steps during the conversion process would be to convert these statements first.  Although there is a way to introduce concatenated statements in VB/VBA, it is felt that the above methodology is just as easy, and provides for a more clear representation of complex If...then statements.

 

"Iterative or Loop" Statements:             Another syntax issue is that of the iterative statements such as the “while”, “for each”, “for” and the like.  So that, once the “if...then” statements have been taken care of, the next thing that could be tackled would be the iterative statements.  Although it is understood that each programmer has a personal programming style and approach in performing the conversion, the above points merely illustrate the types of syntax differences, which the programmer will need to address.

 

"Error Trapping":                       Finally, a good feature to take advantage of in the VB/VBA environment, which is not available in Avenue, is the ability to trap errors.  Error trapping provides the developer a means to avoid application runtime errors, which typically result in the application to cease to operate properly.  By avoiding application runtime errors, the application can still be used to perform other functions, should an error be encountered, rather than simply "dying".  This results in giving the application a more professional appearance.  The code below demonstrates the use of error trapping:

              '

              Public Sub ShowErrorTrapping()

              '

              Dim pMxApp As IMxApplication

              Dim pmxDoc As IMxDocument

              Dim pActiveView As IActiveView

              Dim pMap As IMap

              '

              '  ---This statement informs the application where to

              '  ---branch when an error is detected in the procedure

              On Error GoTo Errorhandler

              '

              '  ---Get the active view

              Call avGetActiveDoc(pMxApp, pmxDoc, pActiveView, pMap)

              '

              '  ---do something else

                  .

                  .

                  .

              '

              '  ---At this point, our work is done

              Exit Sub

              '

              '  ---Handle any errors detected in the procedure

              Errorhandler:

              '

              '  ---Display detected error number and a description

              MsgBox "Error " & Err.Number & " - " & Err.Description & _

                     Chr(13) & "Subroutine: ShowErrorTrapping"

              '

              End Sub

 

Note that the introduction of comments in VB/VBA is the same as in Avenue with the exception that comments appear in the color green.  In addition, syntax errors detected in VB/VBA appear in the color red as soon as the typing of a statement has been completed.  This is quite a good feature, but it has a bothersome side effect.  In addition to displaying the erroneous statement in red, VB/VBA also beeps and displays an error message box, which must be closed before the user can continue typing.

 

3. Application - Document Avenue Wraps

 

In Avenue the very first statement that typically appeared in a script was:

 

              theView = av.GetActiveDoc

 

The object, theView, gave the developer access to all of the themes in the view.  For example, a list of visible themes in the view could be ascertained by the statement:

 

              vThemesList = theView.GetVisibleThemes

 

Using Avenue Wraps the above statements would appear as:

 

              Dim pMxApp As IMxApplication

              Dim pmxDoc As IMxDocument

              Dim pActiveView As IActiveView

              Dim pMap As IMap

              Dim vThemesList As New Collection

              Call avGetActiveDoc(pMxApp, pmxDoc, pActiveView, pMap)

              Call avGetVisibleThemes(pmxDoc, vThemesList)

 

The first point that needs to be made, in reviewing the above code, is that in Avenue a developer applied a request to an object, which resulted in another object being created, vThemesList in the above Avenue example.  This approach is not possible using ArcObjects.  Therefore, to simulate the Avenue approach, Avenue Wraps provides a subroutine or function to perform the function of the Avenue request by using the argument list in the subroutine or function to pass objects in and out.

 

The second point is that a List object in Avenue translates to a Collection object in VB/VBA.  Note that List objects in Avenue are zero based, while Collections begin at the index value 1.  For example, the first visible theme in vThemesList would be found by issuing the following statement:

 

              firstVTheme = vThemesList.Get(0)

 

while in VB/VBA the statement would appear as:

 

              firstVTheme = vThemesList.Item(1)

 

Note that the Avenue request Get becomes Item in VB/VBA.  So that, the search for and revision of lists into collections, the change of Get to Item, and the altering of the indexing could be done at the start of the conversion process.

 

The final point is that the Avenue code contains two lines of code, while the VB/VBA code contains seven, the first five of which are declaration statements.  Thus, even though there is a one to one correspondence between the Avenue requests and the “wraparounds”, the number of lines of VB/VBA code is greater than its Avenue counterpart due to the declaration statements.  To make the VB/VBA code easier to read, it is suggested that all of the declaration statements be placed at the top of the procedure, rather than scattered throughout the subroutine or function.  One procedure, which one may consider, is to introduce the declaration statements within the code where new variables are first encountered, and then, after the code has been tested, move the declaration statements to the top of the procedure in the same sequence of occurrence.

 

4. File Input and Output Avenue Wraps

 

In dealing with file I/O, it is primarily native VB/VBA code that is used to accomplish reading and writing of files.  ArcObjects itself does not provide functionality to read and write files.  As such, the "wraparounds" found in Avenue Wraps are VB/VBA code which provide the functionality found in their Avenue counterparts.

 

For example the Avenue statement to open a file for writing would appear as:

 

              aLineFile = LineFile.Make (aFileName, #FILE_PERM_WRITE)

 

while using Avenue Wraps, the statement would look like this:

 

              Dim aFileName As String

              Dim aLineFile

              Set aLineFile = avLineFileMake(aFileName, "WRITE")

 

Note the use of the word Set.  In VB/VBA when dealing with objects the keyword Set is used to create an object.  In this example, aLineFile is a file system object.  In addition, in Avenue there were enumerations (predefined values or constants), which certain requests recognized.  Avenue Wraps simulates enumerations by using strings that reflect the enumeration, such as "WRITE" for "FILE_PERM_WRITE".

 

5. Theme and Table Avenue Wraps

 

This is the heart of any application.  This is where the developer manipulates the data in a table.  In Avenue the developer would operate on an FTab or VTab object depending upon the type of table being processed.  Using ArcObjects, the developer uses:

         The IFeature interface, if dealing with an FTab, or,

         The IRow interface, if dealing with a VTab.

To avoid having to create a separate set of "wraparounds", one for FTab's and one for VTab's, most of the "wraparounds" have been written to process both FTab's and VTab's by having the user pass the name of the theme or table to be processed, rather than, passing an FTab or VTab object.

 

For example the following Avenue code writes a value, 24, to a specific record, 12, in an FTab:

 

              theView = av.GetActiveDoc

              theTheme = theView.FindTheme("L_0ln")

              theFTab = theTheme.GetFTab

              col = theFTab.FindField("MAP")

              rec = 12

              theFTab.SetEditable(true)

              theFTab.SetValue(col, rec, 24)

              theFTab.SetEditable(false)

 

Using Avenue Wraps, the above code could be converted as follows:

 

              Dim pMxApp As IMxApplication, pmxDoc As IMxDocument

              Dim pActiveView As IActiveView, pMap As IMap

              Dim theTheme As String

              Dim theFTab As IFields

              Dim pFeatCls As IFeatureClass, pLayer As IFeatureLayer

              Dim col As Long, rec As Long

              Call avGetActiveDoc(pMxApp, pmxDoc, pActiveView, pMap)

              theTheme = "L_0ln"

              Call avGetFTab(pmxDoc, theTheme, theFTab, pFeatCls, pLayer)

              col = theFTab.FindField("MAP")

              rec = 12

              Call avSetEditable(pmxDoc, theTheme, true)

              Call avSetValue(pmxDoc, theTheme, col, rec, 24)

              Call avSetEditable(pmxDoc, theTheme, false)

 

The Avenue Wraps code shown above could be used on either an FTab or a VTab, because the "wraparounds" check if the given name passed in corresponds to a theme or to a table.  Note that the FindField request has a direct counterpart in the ArcObjects world.  This is one of the very, very few examples where a "wraparound" was not required.

 

As stated above, the "wraparound", avSetValue, operates on either an FTab or a VTab.  However, it cannot be used to store geometry (a feature's shape).  In this case the "wraparound", avSetValueG, must be used.  The reason is that when storing attribute information with avSetValue, the ArcObjects Value property is used.  However, in order to store geometry (a feature's shape), the ArcObjects Shape property needs to be used.  Since there are two different properties, two different “wraparounds” were created, one to store attribute data and the other to store geometry.

 

Continuing with the example above, the Avenue code below would store a two-point line in record 12 of the FTab:

 

              aShape = Polyline.Make({{20000.0, 20000.0, 30000.0, 25000.0}})

              col = theFTab.FindField("SHAPE")

              theFTab.SetEditable(true)

              theFTab.SetValue(col, rec, aShape)

              theFTab.SetEditable(false)

 

Using Avenue Wraps, the above code could be converted as follows:

 

              Dim aShape As IPolyline

              Dim col As Long

              Set aShape = avPolyline2Pt(20000#, 20000#, 30000#, 25000#)

              col = theFTab.FindField("SHAPE")

              Call avSetEditable(pmxDoc, theTheme, true)

              Call avSetValueG(pmxDoc, theTheme, col, rec, aShape)

              Call avSetEditable(pmxDoc, theTheme, false)

 

In reviewing the above Avenue Wraps code, note the # character in the "wraparound" avPolyline2Pt.  When a number is hard coded with .0, .00, .000, etc., VB/VBA will remove the zeros and replace them with the # character.  In addition, note that there is no direct Avenue counterpart to the "wraparound", avPolyline2Pt.  During CEDRA's conversion process, in an effort to minimize the amount of recoding, certain "wraparounds" were developed that do not have corresponding Avenue requests, but satisfy a need in that they facilitate the conversion process.  In the CEDRA software there were a lot of cases in which two point lines were being created using the double left ({{) and the double right (}}) brace characters.  Rather than using the "wraparound", CreateList and adding point features to the lists (reference is made to the Section 8 example), the "wraparound", avPolyline2Pt was created.

 

As a matter of fact, there are a number of "wraparounds" which do not have an Avenue counterpart, but when used, greatly help in the conversion of Avenue to VB/VBA code.

 

Finally, a good feature to take advantage of is the Undo / Redo capabilities of ArcObjects.  The “wraparound”, avSetEditable, when used with the false parameter on a theme will not terminate the editing of the theme.  Rather, the “wraparound” only flushes any buffered writes there may be on the theme.  Thus, the theme remains in an editable state.  This is intentionally done so that the developer can take advantage of the Undo / Redo capabilities.  To do so, the “wraparounds”, avStartOperation and avStopOperation, should be used.  These “wraparounds” begin and terminate an edit operation which is added to the Editor’s operation stack.  This enables the end user to perform an Undo or Redo operation.  The avStartOperation subroutine is called after the theme has been made editable.  The avStopOperation subroutine is called when the desired editing has been completed.  A call to the avStopOperation subroutine must be made if a call to the avStartOperation subroutine has been issued.  There is no limit to the number of times these subroutines can be called within an edit session.  The “wraparound”, avStopEditing, will terminate the Editor saving any edits that may have been made.

 

6. Feature Selection Avenue Wraps

 

The "wraparounds" in this section follow the convention in the previous section in that the developer passes the name of the theme or layer to be processed rather than an FTab or VTab object.  For example, the following Avenue code could be used to cycle through the selected set of features in a theme to compute a total value for a field called Deposits:

 

              theView = av.GetActiveDoc

              theTheme = theView.FindTheme("L_0ln")

              theFTab = theTheme.GetFTab

              col = theFTab.FindField("Deposits")

              sel = theFTab.GetSelection

              total = 0.0

              for each rec in sel

                  deposit = theFTab.ReturnValue(col, rec)

                  total = total + deposit

              end

 

Using Avenue Wraps, the above code could be converted as follows:

 

              Dim pMxApp As IMxApplication, pmxDoc As IMxDocument

              Dim pActiveView As IActiveView, pMap As IMap

              Dim theTheme As String

              Dim theFTab As IFields

              Dim pFeatCls As IFeatureClass, pLayer As IFeatureLayer

              Dim col As Long

              Dim sel As ISelectionSet, selList As New Collection

              Dim total As Double, iRec As Long, rec As Long

              Dim pFeat As IFeature

              Dim deposit As Double

              Call avGetActiveDoc(pMxApp, pmxDoc, pActiveView, pMap)

              theTheme = "L_0ln"

              Call avGetFTab(pmxDoc, theTheme, theFTab, pFeatCls, pLayer)

              col = theFTab.FindField("Deposits")

              Call avGetSelection(pmxDoc, theTheme, sel)

              Call avGetSelectionIDs(sel, selList)

              total = 0#

              For iRec = 1 to selList.Count

                  rec = selList.Item(iRec)

                  Set pFeat = pFeatCls.GetFeature(rec)

                  deposit = pFeat.Value(col)

                  total = total + deposit

              Next

 

In reviewing the above Avenue Wraps code, note the use of the "wraparound", avGetSelectionIDs.  This "wraparound" returns a collection of OIDs (object identification numbers) for each of the selected features.  So that, rather than looping on the selected set, sel, as was done in Avenue, a loop is performed on the collection, selList.  An OID is extracted from selList and a feature extracted from the pFeatCls object.  Using the feature, pFeat, the deposit value is extracted and added to total.  This example illustrates the main difference between Avenue and ArcObjects, in that, in Avenue manipulation of data was performed on FTab or VTab objects.  Using ArcObjects, manipulation of data is performed using the IFeature interface, for FTab's, and the IRow interface, for VTab's.  Obviously, this difference poses an obstacle in converting Avenue code, but using the "wraparound", avGetSelectionIDs, a work-around is fairly easily achieved.

 

7. Message and Menu Boxes Avenue Wraps

 

This was the portion of the conversion that was probably the easiest to deal with.  Basically what was done was to create a single form, and then programmatically alter the form based upon the type of dialog box that needed to be displayed.  For example, the Avenue Info request which appeared as:

 

              MsgBox.Info("A sample message string.", "MsgBox Test")

 

would be converted using Avenue Wraps as:

 

              Call avMsgBoxInfo("A sample message string.", "MsgBox Test")

 

Internally, avMsgBoxInfo calls another subroutine passing information, which denotes that the Info request is to be simulated.  By taking the approach to programmatically modify a single form, we avoid the alternative of having to create a unique form for every type of dialog box that is used in the CEDRA software.  So that, the Avenue requests Warning, YesNo and YesNoCancel, as well as Info, all end up calling the same subroutine which in turn uses the same Avenue Wraps form, MessageBox.  Likewise, the other Avenue message box requests, Choice, Input, List, MultiInput and MultiList all end up using the Avenue Wraps form, VDialogBox.  Thus, based upon what information is passed into the controlling subroutine, the appropriate dialog box is displayed.

 

In addition, to simply the conversion of the standard MsgBox requests, additional "wraparounds" were developed to provide for more sophisticated dialog boxes, such as dialog boxes that could contain "dr