Development of generic, reusable ActiveX GIS components based off of Esri’s ARC/INFO Open Development Environment (ODE).

 

Timothy Miller, Eric Songer, Joseph Huss, Micheal McInnis, John Rourke Jr., Mary Kruck, Leo Salemann, Durwood Gafford, and Steven Prager, Lockheed Martin Information Systems, Advanced Simulation Center

 

Abstract

Esri’s ARC/INFO Open Development Environment (ODE) provided a powerful and flexible alternative to developing custom applications with AML macros. Both ODE and AML environments executed the ARC/INFO command line and provided access to AML functions and directives. Developing ODE applications as with AMLs still required a good degree of ARC/INFO and AML experience and was difficult for non-ARC/INFO users to develop and maintain. A generic set of ActiveX Objects were created that encapsulated ARC/INFO’s GIS functionality into groups of classes/objects that would promote ease of use for non-ARC/INFO users, and still have power, flexibility, and promote reuse for the traditional GIS users.

 

Introduction

The ARC/INFO Open Development Environment Application Programmer’s Interface (ARCODEAPI) was developed as a COM/ActiveX group of objects that provide ARC/INFO command line functionality for use in developing Microsoft Windows based applications1. The goal was to create an application programmer’s interface (API) that promotes ease of use for non-ARC/INFO users, yet still have power and flexibility, and provide an alternative to AMLs.

ARCODEAPI was implemented using Microsoft Visual Basic and is packaged as an ActiveX DLL. We chose to develop ARCODEAPI in Microsoft Visual Basic for several reasons:

  1. Esri had already built ARC/INFO ODE objects based on Visual Basic; we wanted to reuse/base our object model off this existing model.
  2. We have a talented GIS staff with diverse backgrounds and various degrees of programming experience. We wanted to choose a language that was object oriented, robust, and had a minimal learning curve.
  3. ARCODEAPI is essentially a wrapper around the ARC/INFO command line interface. ARC/INFO is still doing the bulk of the processing and the compiled speed of the binary API was not a critical factor.

The ARCODEAPI functionality can be categorized into four functional areas:

  1. Data Management Objects – Manages ARC/INFO Coverages, Grids, Workspaces and ARC/INFO supported image formats.
  2. Coverage Objects – Manipulates and processes ARC/INFO Coverages.
  3. Raster/Image Objects – Manipulates and processes ARC/INFO Grids and ARC/INFO supported images.
  4. Projection Objects – Projects ARC/INFO Coverages, Grids, and generic geometric objects.

Figure 1 ARCODEAPI Object Overview

Data Management Objects

The data management objects are designed to create, track, and manage vector and raster data (ARC/INFO Coverages, Grids, and supported Image formats). The data management objects are hierarchical collections. Navigating through the object hierarchy provides a simple and useful mechanism for doing repetitive manipulations on large quantities of spatial data. The following Visual Basic code example shows how easy it is to provide access to all the Coverages within a Vector Library Object.

' Loop through all the tiles in the source vector library
For Each SrcTile In SourceLibrary.Tiles
   ' Loop through all of the geodatasets within the tile
   For Each SrcGeoDataSet In SrcTile.GeoDataSets 
      ' Loop through all the geodata collected
      For Each SrcObject In SrcGeoDataSet
         ' If the object is type clsCoverage project it
         If TypeOf SrcObject Is clsCoverage Then
            ' Do some thing with the coverage
         End If
      Next SrcObject
   Next SrcGeoDataSet
Next SrcTile
  

Figure 2 ARCODEAPI Data Management Objects

Internal Data Management

A Microsoft Access database (GeoProject Database) is used to internally track all data objects present in the ARCODEAPI. The tables are used for saving/retrieving object persistence and allow searching for specified objects within the project. As most object properties are populated upon instantiation, only the properties for object instantiation, searching, and status are present in the tables.

The ARCODEAPI objects communicate with the GeoProject Database through the private GeoProjectDBAccess object. The GeoProjectDBAccess object is based on Microsoft’s Data Access Object (DAO), and provides an SQL based mechanism for ARCODEAPI objects to add, delete, update, and query the tables (see Table 1) in the GeoProject Database.

Figure 3 GeoProjectDBAccess

Table 1 The GeoProject Database consists of six tables.

Table Name

Table Fields

Coverage Table CoverageName, CoverageFullPath, XMin, Ymin, Xmax, Ymax, GeoDataSetName, TileName, LibraryName, CoverageType, EditStatus, QualityCheck
GeoDataSet Table GeoDataSetName, LibraryName, Count, TileName, WorkspacePath
Tile Table TileName, TileFullPath, Xmin, Ymin, Xmax, Ymax, LibraryName, GeoDataSetCount
Vector Library Table LibraryName, LibraryBasePath, TileCount, Xmin, Ymin, Xmax, Ymax, TileRef, Description, LibraryType
Image Catalog Table ImageCatalogName, ImageCatalogFullPath, XMin, YMin, Xmax, Ymax, ImageType
Image/Raster Table ImageRasterName, ImageRasterFullPath, Type, Xmin, Ymin, Xmax, Ymax, ImageCatalogName, GeoDataSetName, QualityCheck

Raster Data Management

Raster data management is similar to vector data management and uses the ARC/INFO Image Catalog system, maintaining object persistence information within the GeoProject Database. The raster data management consists of two objects, RasterCatalogs and RasterCatalog, that are used to manage and track ARC/INFO Grids (RasterGrid object) and images (RasterImage object) supported by ARC/INFO (see "Raster/Image Objects").

The RasterCatalog object is analogous to an individual ARC/INFO Image Catalog and is actually an object interface to an image catalog. The RasterCatalog also offers the ability to set the catalog type (e.g., A catalog can be set to collect TIFF images, and the RasterCatalog object will only allow TIFFs to added), query the catalog extents, or see if an image exists within the catalog. The RasterCatalog object also provides the COM NewEnum interface to support "For Each" looping through each image object collected in the RasterCatalog Object. For example, the following Visual Basic code converts all images within a RasterCatalog to Grids and adds them to a new RasterCatalog.


Dim InRasterCatalog As clsRasterCatalog
Dim OutRasterCatalog As clsRasterCatalog
Dim RasterImage As clsRasterImage
' Create a new Catalog
Set OutRasterCatalog = New clsRasterCatalog
OutRasterCatalog.CreateCatalog "C:\data\grids", "MyGrids", _
                               ArcGrid
' Convert the old image to grids and add to the new catalog.
For Each RasterImage In InRasterCatalog
   OutRasterCatalog.Add RasterImage.ConvertToGrid _
                        ("C:\data\grids" RasterImage.ImageName)
Next
    

The RasterCatalogs object collects RasterCatalog objects and is used to manage/track all the RasterCatalog objects (ARC/INFO Image Catalogs). The following Visual Basic code illustrates "For Each" looping through the RasterCatalogs object.


' Loop through all the catalogs being collected in RasterCatalogs
For Each RasterCatalog In RasterCatalogs
   If RasterCatalog.RasterType = ArcGrid Then
      ' Loop through all the Grids in the Catalog
         For Each RasterGrid In RasterCatalog
            ' Do some thing with the grid
         Next
   Else
      ' Loop through all the Images in the Catalog
      For Each RasterGrid In RasterCatalog
         ' Do some thing with the image
      Next
   End If
Next
    

Figure 4 Raster Data Management Objects

Vector Data Management

The vector data management objects are used to manage ARC/INFO Coverages (see Coverage Object Section) and Workspaces. ARCODEAPI manages vector coverages as objects. The object hierarchy organizes the ARC/INFO Workspaces and Coverages, and keeps track of where coverages are physically stored. Workspaces and Coverages can be scattered across various disks and directory structures and ARCODEAPI organizes them into a consistent useable hierarchy. Note that ARCODEAPI is not a wrapper around ARC/INFO LIBRARIAN, but does support the use of a similar tiling schema and data hierarchy.

The vector data management object hierarchy consists of:

  • VectorLibraries – A collection object that holds VectorLibrary Objects;
  • VectorLibrary – An instance of a single Library;
  • Tiles – A collection object that holds all the tiles of a VectorLibrary;
  • Tile – An instance of a single tile within a Library;
  • GeoDataSets – A collection object that holds multiple GeoDataSet objects, and;
  • GeoDataSet – A collection object that holds ARC/INFO GeoData: Coverages, Images, and Grids.

VectorLibraries / VectorLibrary

The GeoProject object (the top of the object model hierarchy) contains a VectorLibraries object. VectorLibraries is a collection object that provides general collecting capabilities and "For Each" looping of VectorLibrary objects.

The VectorLibrary object provides an interface for creating, querying, and manipulating data within a Vector Library. The primary purpose of the VectorLibrary object is to provide access to the tiled data. The tileddata can be accessed through various Get-Methods (GetCoverage, GetCoverages, GetTile (by name), or GetTiles (by extent), or through the Tiles property. Other methods include:

  • Project – projects an entire Vector Library; and
  • UpdateGeoProject – saves the current VectorLibrary properties to the GeoProject Database (present in all objects that have an associated entry within the GeoProject Database).

The VectorLibrary object also provides information/metadata about the library: number of tiles, number of coverages, spatial extent, the projection, and other textual information (Library Name and Library Description).

Figure 5 VectorLibraries / VectorLibrary Objects

Tiles / Tile

The Tiles collection object provides general collecting capabilities and "For Each" looping of the Tile Object.

The Tile objects primary purpose is to hold the GeoDataSets collection object and provide information/metadata about the tile.

Figure 6 Tiles / TileObjects

GeoDataSets / GeoDataSet

The GeoDataSets collection object provides general collecting capabilities and "For Each" looping of GeoDataSet objects.

The GeoDataSet object is the primary spatial data collection object. Its main purpose is to collect ARC/INFO Coverages; However, it is also capable of collecting ARC/INFO Grids and Images. As a general collection object it provides properties and methods for managing the objects within the collection. The GeoDataSet object can be used to collect data from various ARC/INFO Workspaces or can be tied to an individual Workspace. When the GeoDataSet object collects data from a single ARC/INFO Workspace the AILog and Workspace objects are instantiated.

The AILog collection object collects AILogRecord objects, and has methods for adding comments and clearing the ARC/INFO log file. The AILogRecord object contains properties for each item in the ARC/INFO log, and for each row in a log, an AILogRecord object exists.

The Workspace object provides properties and methods for managing and manipulating ARC/INFO Workspaces. This includes:

  • The ability to create, delete, copy, and rename ARC/INFO Workspaces;
  • The ability to create new Coverages;
  • The ability to delete ARC/INFO Coverages, Grids, TINs, Stacks, and INFO tables; and
  • The ability to list Workspace Coverages, Grids, TINs, Stacks, Files, and Images.

Figure 7 GeoDataSets / GeoDataSet Objects

Coverage Objects

The Coverage object provides information/metadata and functionality to manipulate ARC/INFO Coverages. The Coverage object is based on Esri’s ODE Coverage object and has been augmented to provide additional functionality. The Coverage object provides access to four groups of objects:
  1. Esri’s ODE Coverage Level Objects,
  2. Coverage Operation Objects,
  3. Editing Objects, and
  4. Coverage Geometry Objects.

Figure 8 Coverage Object

Esri’s ODE Coverage Level Objects

The coverage level objects: FeatureTables, FeatureTable, Tolerance, and Extent are all based on the Esri ODE objects. With the exception of the Tolerance object, the objects have additions and interface modifications. For Example, the FeatureTables object had the NewEnum interface added to support "For Each" looping of FeatureTable objects, and the Extent object has additional interfaces for geometric bounding area checks. The FeatureTable object has a new INFOFields property that provides access to the coverage’s attributes through the items in the table (see Editing Objects).

Figure 9 Coverage Level Objects

Coverage Operation Objects

The coverage operation objects are a group of objects that provide functionality to execute Coverage level operations that are otherwise available at the ARC command prompt. The operation objects are divided by geometric feature class type: Line (LineCoverageOperator), Polygon (PolygonCoverageOperator), Node (NodeCoverageOperator), and Point (PointCoverageOperator). Three other objects allow manipulation of the coverage’s INFO tables: INFOTable, INFOTableFrequency, and INFOTableStatistics. Common functionality that is present in more than one of the coverage operation objects is coded in a private object (CommonCoverageOperator) and exposed publicly in the operations objects.

The OperationFeatureType property in the Coverage object establishes which operations object will be available through the AIOperation property. For Example, the following Visual Basic code will buffer a line coverage:


Dim Coverage As ARCODEAPI.clsCoverage
Dim BufferCov As ARCODEAPI.clsCoverage
Set Coverage = New ARCODEAPI.clsCoverage
Coverage.DataSource = "C:\Data\Roads"
'  Buffer the roads
Coverage.OperationFeatureType = Arc
Set BufferCov = Coverage.AIOperation.Buffer(OutputDataSource:= _ 
                                            "C:\Data\RoadBuf", _
                                            BufferDistance:=10)

As soon as the coverage datasource property is set, the coverage object updates all relevant coverage properties. Then, the OperationFeatureType is set to arc, causing the AIOperation property to be set to instantiate and bind to the LineCoverageOperator object.

The above methodology results in a highly flexible data structure that can be dynamically reconfigured depending on the type of coverage operation desired. If, for example, the user wishes to build and manipulate node topology for the original Arc coverage, the OperationFeatureType is set to node and the appropriate commands are invoked via the Coverage.AIOperation property.

Figure 10 Coverage Operation Objects

Editing Objects

Coverage editing objects are a group of objects that provide ARCEDIT functionality to ARCODEAPI. The EditFeature object is the primary access point for using the Edit objects. The EditFeature object provides the properties and methods for controlling (selecting, unselecting, deleting, etc.) and manipulating the features that are being edited. Either Coverage features or INFO table records may be edited. Depending on the specified editfeature, the EditFeature objects bind the appropriate object from the group of objects for manipulating geometric features: EditLineFeature, EditPolygonFeature, EditPointFeature, and EditNodeFeature. These objects provide functionality to move, copy, create, and edit features (Lines, Points, Nodes and Polygons). The EditFeature objects also provide functionality for controlling the relate (Relate and Relates objects) and snapping (EdgeSnap and SnapFeatures objects) environments within ARCEDIT and provide item definitions for INFO Tables (INFOField and INFOFields objects).

The Coverage object has an OpenEdit method that opens the Coverage for editing and sets the edit feature within ARCEDIT (similiar to the commands, Edit <Coverage>; Editfeature <feature type> in ARCEDIT). OpenEdit also populates the properties in the EditFeature object. The Feature property with the EditFeature objects can be set to expose the EditLineFeature, EditPolygonFeature, EditPointFeature, or EditNodeFeature objects through which editing will be accomplished.

Like the coverage operation objects above, the edit objects are also highly flexible and can be dynamically reconfigured at any point in the operation.

The following is an example of Visual Basic Code to edit an arc coverage.

Figure 11 Coverage Edit Objects


Dim Coverage As ARCODEAPI.clsCoverage
Set Coverage = New ARCODEAPI.clsCoverage
Coverage.DataSource = "C:\Data\Roads"
Call Coverage.OpenEditFeature(Arc)
Coverage.EditFeature.SelectedSetProcessingMode = SelectMode_Cursor
' Select all roads of type 2, Paved.
Call Coverage.EditFeature.CreateSelectedSet("SELECT ROAD-TYPE = 2")
' If no feature are paved exit.
If Coverage.EditFeature.NumberRecordsSelected = 0 Then
   Coverage.EditFeature.RemoveEdit
   Exit Sub
End If
' Loop through each feature in the selected set and generalize
Do While Not Coverage.EditFeature.EOF
   With Coverage.EditFeature
      Call .Feature.Generalize(Feature.Item("Width") * 0.00045 * _
            Feature.Item("Length"), Enum_GeneralizeMethod.BendSimplify)
      .MoveNextRecord
   End With
Loop
Coverage.EditFeature.Save
Coverage.EditFeature.RemoveEdit

Coverage Geometry Objects

The geometric objects provide read only access to the Coverage's geometry, and are accessed through the Geometry property within the Coverage object. The Geometry object provides methods to return a collection of geometric objects: Node, Point, Line, or Polygon. The Node, Point, Line, and Polygon objects then contain groups of primitive geometric objects that actually contain coordinate information. The primitive geometric objects are modeled after Esri's MapObjects geometric objects (Parts, Points, Point). This mimicking of MapObjects objects allows generic geometric methods to be reused with both the ARCODEAPI and Esri's MapObjects.

The following Visual Basic code writes an ARC/INFO ASCII Generate file using the Geometry Objects:


Dim Coverage As ARCODEAPI.clsCoverage
Dim Line As ARCODEAPI.clsLine
Dim Point As ARCODEAPI.clsPoint
Dim TextStream As Scripting.TextStream
Dim FileSys As Scripting.FileSystemObject
Set Coverage = New ARCODEAPI.clsCoverage
Set FileSys = New Scripting.FileSystemObject
Set TextStream = FileSys.OpenTextFile("C:\data\road.gen", _
                                      ForWriting, True)
Set FileSys = Nothing
Coverage.DataSource = "C:\data\road"
Call Coverage.Geometry.GetGeometry(ArcLine)
For Each Line In Coverage.Geometry
   TextStream.WriteLine Line.ARCID
      For Each Point In Line.Parts.Item(1)
         TextStream.WriteLine Point.X &  " " & Point.Y
      Next
   TextStream.WriteLine "END"
Next
TextStream.WriteLine "END"
TextStream.Close
  

Figure 12 Coverage Geometric Objects

Other Coverage Operations

Two other coverage level operator objects are available, but are not associated with the object hierarchy: MergeCoverages and SimpleToComplex. The MergeCoverages object is a wrapper around the Arc Append command. The object collects Coverage objects that will be merged with the Arc Append command. SimpleToComplex is used for merging Coverages with Arc features to a Coverage with Route features, and Coverages with Polygon features to a Coverage with Region features.

Figure 13 Other Coverage Operations Objects

Raster/Image Objects

ARCODEAPI’s RasterGrid and RasterImage objects are extended versions of Esri’s ODE Grid and Image objects. Additional functionality has been added.

Raster Grid Object

The RasterGrid object has several interface changes:
  1. Properties were added to track RasterCatatlog or GeoDataSet objects;
  2. Properties were added for ??ERROR?? handling and processing.
  3. Methods were added to support import and export in several formats;
  4. Methods were added to manipulate Raster Grid extents (Clip, Flip, and Shift).

RasterGrid Code Example:


' Converts a USGS DEM to an GRIDASCII file.
Dim Grid As ARCODEAPI.clsRasterGrid
Dim Workspace As ARCODEAPI.clsWorkspace
Set Grid = New ARCODEAPI.clsRasterGrid
Set Workspace = New ARCODEAPI.clsWorkspace
Workspace.Workspace = "F:\Workspace"
Grid.ImportUSGSDEM("F:\Workspace\d45112.dem", _
                   "F:\Workspace\d45112") _
                  .ExportASCII "F:\Workspace\d45112.asc" 
Workspace.DeleteGrid "d45112"
    

Figure 14 RasterGrid Object

Raster Image Object

Several interface changes were made to the RasterGrid object:
  1. Properties were added to track RasterCatatlog or GeoDataSet objects;
  2. Properties were added for handling and processing; and
  3. The interface to FeatureTables/FeatureTable was removed.

While the Raster Image object is redundant in many regards, it does provide a specialized environment specifically dedicated to managing images. The overhead associated with managing raster grids is not present, and the image objects are thusly optimized as they pass through the system.

Figure 15 RasterImage Object

Projection Objects

The projection objects provide the functionality to project ARC/INFO Coverages , Grids, and the Points and Point geometric objects. To accomplish this, the projection definition object must be able to store and manipulate projection parameters. Currently, six projection definition objects exist: (1) Geographic, (2) UTM, (3) Albers Equal Area, (4) Lambert Conformal Comic, (5) Transverse Mercator, and (6) Mercator. The Projector object uses the projection definition object to construct the proper project parameters before projecting GeoDataSets.

Visual Basic code example for projection an ARC/INFO Coverage:


Dim Coverage As ARCODEAPI.clsCoverage
Dim PrjCoverage As ARCODEAPI.clsCoverage
Dim Projector As ARCODEAPI.clsProjector
Dim InputPrj As ARCODEAPI.clsPrjGeographic
Dim OutputPrj As ARCODEAPI.clsPrjLambertConformalConic
Set Coverage = New ARCODEAPI.clsCoverage
Set Projector = New ARCODEAPI.clsProjector
Set InputPrj = New ARCODEAPI.clsPrjGeographic
Set OutputPrj = New ARCODEAPI.clsPrjLambertConformalConic
Coverage.DataSource "D:\Data\roads"
' Set the input projection parameters.
InputPrj.Datum = Datum_WORLDGEODETICSYSTEM_1984
InputPrj.Spheroid = Spheroid_WorldGeodeticSystem_1984
InputPrj.Units = MapUnits_DecimalDegrees
' Set the ouyput projection parameters.
OutputPrj.Datum = Datum_WORLDGEODETICSYSTEM_1984
OutputPrj.Spheroid = Spheroid_WorldGeodeticSystem_1984
OutputPrj.Units = MapUnits_Meters
OutputPrj.CentralMeridian = -114
OutputPrj.FirstStandardParallel = 40
OutputPrj.SecondStandardParallel = 44
OutputPrj.LatitudeOfOrigin = 44
OutputPrj.FalseEasting = 500000
OutputPrj.FalseNorthing = 500000
' Project the coverage.
Set Projector.InputProjection = InputPrj
Set Projector.OutputProjection = OutputPrj
Set Projector.GeoDataSource = Coverage
Projector.Precision = DoublePrecision
Set PrjCoverage = Projector.Project("D:\Data\roads")
  

Again, the extensibility of the system is evident. If support for additional projection types is desired, only a new projection definition object is required. Furthermore, the enumerated projection types ensure that the choices available to the programmer are predetermined, preempting the possibility of selecting a projection that is not supported.

Figure 16 Projection Object

Conclusion

ARCODEAPI provides an object oriented collection of data and methods that encapsulates much of the functionality of the ARC/INFO command line interface. The ARCODEAPI interface can be used to develop Microsoft Windows applications and tools, and provides a relatively simple object model wherein GIS and non-GIS developers can easily develop applications that take advantage of the Esri Open Development Environment.

Acknowledgements

ARCODEAPI - development was funded through Contract N61339-95-C-0051.

ARCODEAPI is Copyrighted: (c) 1999-2000 (Copyright held jointly by: Lockheed Martin Corporation; Dynamics Research Corporation; Logicon, Inc.; Science Applications International Corporation; Sterling Software, Inc.) Unlimited rights held by US Government pursuant to Contract N61339-95-C-0051

ARCODEAPI DISTRIBUTION STATEMENT: DISTRIBUTION AUTHORIZED TO THE DEPARTMENT OF DEFENSE AND U.S. DoD CONTRACTORS ONLY DUE TO CRITICAL TECHNOLOGY, EFFECTIVE 20 JUNE 1994. OTHER REQUESTS SHALL BE REFERRED TO THE PCO.

End Notes

1. Development of the Terrain Data Fusion System (TDFS) for Creation of Constructive Simulation Databases. Twentieth Annual Esri International User Conference Paper 796

Author Information (Design/Development Team)

Timothy Miller

Lockheed Martin Information Systems

1182 Hegson Drive

Stevensville, MT 59870

Telephone: 406-777-6039

Fax: 425-746-1335

Email: tmiller@lads.is.lmco.com

Micheal McInnis

Lockheed Martin Information Systems

3605 132nd Ave. S.E., Suite 400

Bellevue, WA 98006

Telephone: 425-957-3248

Fax: 425-746-1335

Email: mmcinnis@lads.is.lmco.com

Leo Salemann

Lockheed Martin Information Systems

3605 132nd Ave. S.E., Suite 400

Bellevue, WA 98006

Telephone: 425-957-3285

Fax: 425-746-1335

Email: lsaleman@lads.is.lmco.com

Eric Songer

Lockheed Martin Information Systems

3605 132nd Ave. S.E., Suite 400

Bellevue, WA 98006

Telephone: 425-957-3225

Fax: 425-746-1335

Email: esongers@lads.is.lmco.com

John Rourke Jr.

Lockheed Martin Information Systems

3605 132nd Ave. S.E., Suite 400

Bellevue, WA 98006

Telephone: 425-957-3241

Fax: 425-746-1335

Email: jrourke@lads.is.lmco.com

Durwood Gafford

Lockheed Martin Information Systems

3605 132nd Ave. S.E., Suite 400

Bellevue, WA 98006

Telephone: 425-957-3219

Fax: 425-746-1335

Email: dgafford@lads.is.lmco.com

Joseph Huss

Lockheed Martin Information Systems

3605 132nd Ave. S.E., Suite 400

Bellevue, WA 98006

Telephone: 850-894-3242

Fax: 425-746-1335

Email: jhuss@lads.is.lmco.com

Mary Kruck

Lockheed Martin Information Systems

3605 132nd Ave. S.E., Suite 400

Bellevue, WA 98006

Telephone: 425-957-3262

Fax: 425-746-1335

Email: mkruck@lads.is.lmco.com

Steven D. Prager *
6102 Olympic Dr. NE
Tacoma, WA 98422

Telephone: 425-957-3290

Fax: 425-746-1335

Email: sdprager@sfu.ca

* Contractor