Getting a Feature Layer by Name

June 3, 2009

An often used function is to get a specific layer from a current map document. The code below is for VB.NET and requires a map document and the alias name of the feature class. The first function returns all IGeoFeature layers in a map – these are layers based on vector geographic data. The second loops through these layers and compares the layer names.

Public Function GetFeatureLayers(ByRef pMap As IMap) As IEnumLayer

Dim pUID As New UIDClass
pUID.Value = "{E156D7E5-22AF-11D3-9F99-00C04F6BC78E}" 'IGeoFeatureLayer GUID

If pMap.LayerCount > 0 Then
GetFeatureLayers = pMap.Layers(pUID, True)
Else
Return Nothing
End If

End Function

Public Function GetFeatureLayerByAliasName(ByRef pMap As IMap, _
ByVal strLayerModelName As String) As IFeatureLayer

Dim pLayerEnum As IEnumLayer
Dim pFLayer As IFeatureLayer

pLayerEnum = GetFeatureLayers(pMap)
If pLayerEnum Is Nothing Then
Return Nothing
End If

pFLayer = DirectCast(pLayerEnum.Next, IFeatureLayer)

Do Until pFLayer Is Nothing
If pFLayer.Valid Then
If strLayerModelName = pFLayer.FeatureClass.AliasName Then
Return pFLayer
End If
End If
pFLayer = DirectCast(pLayerEnum.Next, IFeatureLayer)
Loop

'the layer was not found
Return Nothing

End Function

Rather than using the layer name visible in the table of contents which is easly changed by a user, I use the .AliasName of the feature class. This is automatically set to the name of the layer when added to a map. A user is much less likely to change this. There is however another name that is 100% not to change, it does however require an additional step when adding the layer shown below:

Dim pModelInfo As IModelInfo
pModelInfo = CType(pFLayer.FeatureClass, IModelInfo)
pModelInfo.ModelName = “AnyValueYouLike”

The code to find this layer is similar to the FindFeatureLayer, but relies on the IModelInfo interface.

 Public Function GetFeatureLayerByModelName(ByRef pMap As IMap, _
ByVal strLayerModelName As String) As IFeatureLayer

Dim pLayerEnum As IEnumLayer
Dim pFLayer As IFeatureLayer
Dim pModelInfo As IModelInfo

pLayerEnum = GetFeatureLayers(pMap)
If pLayerEnum Is Nothing Then
Return Nothing
End If

pFLayer = DirectCast(pLayerEnum.Next, IFeatureLayer)

Do Until pFLayer Is Nothing
If pFLayer.Valid Then
pModelInfo = DirectCast(pFLayer.FeatureClass, IModelInfo)
If strLayerModelName = pModelInfo.ModelName Then
Return pFLayer
End If
End If
pFLayer = DirectCast(pLayerEnum.Next, IFeatureLayer)
Loop

'the layer was not found
Return Nothing
End Function
Advertisement

Hiding Geodatabase Tables

April 3, 2008

The following VBA function hides all on the geodatabase tables added by ESRI into an Access database. Useful if you spatially enable your users database, and they wonder what all those GDB_ tables are for..

GDB Tables

To unhide just change the Trues to False, or pass this in as a variable.Public Sub HideGeoDatabaseTables()

Dim strTableName As String For i = 0 To CurrentDb.TableDefs.Count - 1
    If CurrentDb.TableDefs(i).Name Like "GDB_*" Then
        strTableName = CurrentDb.TableDefs(i).Name
        Access.SetHiddenAttribute acTable, strTableName, True
    End If
Next
Access.SetHiddenAttribute acTable, "SelectedObjects", True
Access.SetHiddenAttribute acTable, "Selections", True
End Sub
To view hidden objects in Microsoft Access, go to Options and check the “Hidden Objects” box.

Access Options

Convert C# to VB.Net

September 2, 2007

It can be frustrating when a code sample that seems to do exactly what you want is written in C#, and your application is in VB.Net. http://www.kamalpatel.net/ConvertCSharp2VB.aspx solves this problem by converting the C# code directly into its VB.Net equivalent – all online and for free for code up to 6.15KB in size, and for larger amounts of code a tool can be downloaded for free.

It does the majority of the conversion well but I ran into a few minor problems where line breaks were not taken into account.

This tool will be even more useful when the ESRI samples, the majority of which are only available in VB6, are updated to .NET…


An End to Database Locks!

August 11, 2007

Locked!

Writing ArcObjects code and using an Access personal geodatabase will result sooner or later in an error along the lines of “Cannot aquire a schema lock because of an existing lock.” Spending hours going through code, making sure objects are disposed of and that all connections closed often makes very little difference. It becomes apparent some of the problems lie deeper than customised code when the same errors occur when using ArcMap or ArcCatalog without any modifications.

I have been using Unlocker, a freeware program, to solve these issues when developing with ArcObjects (or just using ArcMap..). Once installed a simple right click on an ldb or mdb file can remove any file locks, and your code is free to run again. It works with any Windows file or folder so is a useful program to have around even without geodatabases.

More reviews and an alternative download site can be found here.

Unlocker


Adding Coordinates to a Map Layout

May 17, 2007

A recent request by an ArcMap was user was how to add the current coordinates of the dataframe to the map layout in ArcMap. I messed around with grids and labelling for a while before resorting to a VBA script.

Coordinate Text

The code below should copied and pasted into the VBA Editor in ArcMap, and run when the current view is a page layout. The number of decimal places and distances of the text labels from the grid can be changed easily by modifying the relevant variable.

    1 Public Sub AddCornerText()

    2 

    3 Dim pMxDoc As IMxDocument

    4 Dim pActiveView As IActiveView

    5 Dim pLayoutExtent As IEnvelope

    6 Dim strText As String

    7 Dim pMapFrame As IFrameElement

    8 Dim pGraphic As IElement

    9 Dim dblOffset As Double

   10 Dim lngRound As Long

   11 

   12 ‘this is the offset used so the text does not overlap the map

   13 ‘modify according to needs

   14 dblOffset = 0.2

   15 

   16 ‘this is the number of decimal places used for each coordinate value

   17 lngRound = 0

   18 

   19 Set pMxDoc = ThisDocument

   20 Set pActiveView = pMxDoc.FocusMap

   21 

   22 ‘find the map data frame in the layout

   23 

   24 Set pMapFrame = pMxDoc.ActiveView.GraphicsContainer.FindFrame(pMxDoc.ActiveView.FocusMap)

   25 Set pGraphic = pMapFrame

   26 Set pLayoutExtent = pGraphic.Geometry.Envelope

   27 

   28 ‘create the bottom left point and text

   29 strText = Round(pActiveView.Extent.XMin, lngRound) & _

   30 “:” & Round(pActiveView.Extent.YMin, lngRound)

   31 AddPoint pLayoutExtent.XMin – dblOffset, pLayoutExtent.YMin – dblOffset, strText

   32 

   33 ‘create the top left point and text

   34 strText = Round(pActiveView.Extent.XMin, lngRound) & _

   35 “:” & Round(pActiveView.Extent.YMax, lngRound)

   36 AddPoint pLayoutExtent.XMin – dblOffset, pLayoutExtent.YMax + dblOffset, strText

   37 

   38 ‘create the bottom right point and text

   39 strText = Round(pActiveView.Extent.XMax, lngRound) & _

   40 “:” & Round(pActiveView.Extent.YMin, lngRound)

   41 AddPoint pLayoutExtent.XMax + dblOffset, pLayoutExtent.YMin – dblOffset, strText

   42 

   43 ‘create the top right point and text

   44 strText = Round(pActiveView.Extent.XMax, lngRound) & _

   45 “:” & Round(pActiveView.Extent.YMax, lngRound)

   46 AddPoint pLayoutExtent.XMax + dblOffset, pLayoutExtent.YMax + dblOffset, strText

   47 

   48 

   49 pMxDoc.ActiveView.Refresh

   50 

   51 End Sub

   52 

   53 Private Sub AddPoint(dblX As Double, dblY As Double, strText As String)

   54 

   55 Dim pMxDoc As IMxDocument

   56 Dim pPoint As IPoint

   57 Dim pTextElement As ITextElement

   58 Dim pPageLayout As IPageLayout

   59 Dim pElement As IElement

   60 Dim pGContainer As IGraphicsContainer

   61 

   62 Set pMxDoc = ThisDocument

   63 

   64 ‘create the location for the text

   65 

   66 Set pPoint = New Point

   67 pPoint.X = dblX

   68 pPoint.Y = dblY

   69 

   70 ‘create the text element

   71 

   72 Set pTextElement = New TextElement

   73 pTextElement.Text = strText

   74 

   75 ‘add the element to the layout

   76 

   77 Set pPageLayout = pMxDoc.PageLayout

   78 Set pElement = pTextElement

   79 pElement.Geometry = pPoint

   80 Set pGContainer = pPageLayout

   81 pGContainer.AddElement pElement, 0

   82 

   83 End Sub


Another Visual Studio Macro

April 4, 2007

My start up program when working with ArcObjects is nearly always set to ArcMap.exe As my ArcMap installation had recently moved from an F: drive to a C: drive I had to update this for over a dozen projects. There had to be an easier way (!) so I spent some time trying to find out how to accomplish this with a Visual Studio macro. This can easily be altered to set the start up program for a set of projects in a solution, or to another .exe.

    1 Imports System

    2 Imports EnvDTE

    3 Imports VSLangProj

    4 Imports VSLangProj2

    5 Imports VSLangProj80

    6 

    7 Public Module ArcGISMacros

    8 

    9     Sub SetStartProgram()

   10 

   11         Dim proj As Project

   12         Dim config As Configuration

   13         Dim configProps As Properties

   14         Dim prop As [Property]

   15 

   16         For Each proj In DTE.Solution.Projects

   17             If TypeOf proj.Object Is VSLangProj.VSProject Then ‘loop through projects

   18                 config = proj.ConfigurationManager.ActiveConfiguration

   19                 configProps = config.Properties

   20                 prop = configProps.Item(“StartProgram”)

   21                 If prop.Value.ToString() Like “*ArcMap*” Then

   22                     prop.Value = “C:\Program Files\ArcGIS\Bin\ArcMap.exe”

   23                 End If

   24             End If

   25         Next

   26     End Sub

   27 End Module


Migrating .NET Projects from ArcGIS 9.1 to 9.2

April 4, 2007

My Windows XP recently decided to fall apart, so after a few days reinstalling everything I decided it would be a good time to switch from ArcGIS 9.1 to 9.2. The installation went smoothly enough, and after a few more hours I had Visual Studio 2005 up and running as well. With some trepidation I opened up my largest ArcGIS VB solution…1030 errors, let alone warnings! The solution had been developed for ArcGIS 9.1 so I had expected some issues..

After some investigation it became apparent the cause of most errors were:

1. None of the ESRI 9.1 DLLs that my projects referenced were present on my machine. These had all been updated to 9.2

2. The ESRI.ArcGIS.Utility library has been deprecated, and its existing functionality moved to the ESRI.ArcGIS.ADF library.

With regards to the first issue all the libraries still had the same name, but were different versions. Changing the “Specific Name” property of the reference allowed VS to find the library and removed the error. I must have several 100 of these references for several projects so I decided to try out the VS macros to automate this task. To edit and create macros manually go to Tools >> Macros > Macros IDE in Visual Studio (2005). All the subs in this post were created in the same module, and require the following references listed below. Some of these had to be added manually to the “MyMacros” project via the References in the Macros IDE Project Explorer. The VSLangProj80 and VSLangProj2 libraries are in C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PublicAssemblies\

    1 Option Strict On

    2 Option Explicit On

    3 

    4 Imports EnvDTE

    5 Imports VSLangProj

    6 Imports VSLangProj2

    7 Imports VSLangProj80

The following procedure loops through every project in the solution, and then through every reference in the project. If the reference starts with the text ESRI then the “Specific Version” property is set to false.

    2 

    3         http://msdn2.microsoft.com/en-us/library/vslangproj80.reference3.specificversion(VS.80).aspx

    4 

    5         Dim projectItem As ProjectItem

    6         Dim myCodeProject As VSLangProj.VSProject

    7         Dim proj As Project

    8         Dim ref As Reference3

    9 

   10         For Each proj In DTE.Solution.Projects

   11             If  TypeOf  proj.Object Is VSLangProj.VSProject  Then ‘loop through projects

   12                 myCodeProject = CType(proj.Object, VSProject)

   13                 For Each ref In myCodeProject.References

   14                     If ref.Name Like “ESRI.*” Then

   15                         ref.SpecificVersion = False

   16                     End If

   17                 Next

   18             End If

   19         Next

   20 

   21     End Sub

This greatly reduced the number of errors, and was so satisfying I continued playing around with macros and the macro recorder available in VS. OK it may have taken longer than doing all this manually..but where is the fun in that..

The following macros work as follows:

UpdateReferences – runs all the sub macros, passing parameters where appropriate.

ReplaceReference – removes a reference tat is no longer needed, and automatically adds in the new reference. In this example I use it to replace all references (sic) to the deprecated ESRI.ArcGIS.Utility reference with the new ESRI.ArcGIS.ADF reference.

ReplaceNameSpaces – this opens the find / replace dialog with parameters already filled. I commented out the automated execution of this replacement as I sometimes received dialogs while replacing that cause the macro to crash. In this example I replaced all Import declarations with the new ESRI.ArcGIS.ADF reference.

   35     Sub UpdateReferences()

   36 

   37         ChangeSpecificVersions()

   38         ReplaceNameSpaces(“ESRI.ArcGIS.Utility”, “ESRI.ArcGIS.ADF”)

   39         ReplaceReference(“ESRI.ArcGIS.Utility”, “C:\Program Files\ArcGIS\DotNet\ESRI.ArcGIS.ADF.dll”)

   40 

   41     End Sub

   42     Sub ReplaceReference(ByVal strOrigRefName As String, ByVal strNewRefPath As String)

   43 

   44 

   45         Dim projectItem As ProjectItem

   46         Dim myCodeProject As VSLangProj.VSProject

   47         Dim proj As Project

   48         Dim ref As Reference3

   49 

   50         For Each proj In DTE.Solution.Projects

   51             If TypeOf proj.Object Is VSLangProj.VSProject Then ‘loop through projects

   52                 myCodeProject = CType(proj.Object, VSProject)

   53                 For Each ref In myCodeProject.References

   54                     If ref.Name = strOrigRefName Then

   55                         ref.Remove()

   56                         myCodeProject.References.Add(strNewRefPath)

   57                     End If

   58                 Next

   59             End If

   60         Next

   61 

   62     End Sub

   63 

   64     Sub ReplaceNameSpaces(ByVal strOldNameSpace As String, ByVal strNewNameSpace As String)

   65 

   66         DTE.ExecuteCommand(“Edit.Find”)

   67         DTE.ExecuteCommand(“Edit.SwitchtoReplaceInFiles”)

   68         DTE.Find.Target = vsFindTarget.vsFindTargetFiles

   69         DTE.Find.FindWhat = strOldNameSpace

   70         DTE.Find.ReplaceWith = strNewNameSpace

   71         DTE.Find.MatchCase = False

   72         DTE.Find.MatchWholeWord = True

   73         DTE.Find.MatchInHiddenText = False

   74         DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral

   75         DTE.Find.SearchPath = “Entire Solution”

   76         DTE.Find.SearchSubfolders = True

   77         DTE.Find.KeepModifiedDocumentsOpen = False

   78         DTE.Find.FilesOfType = “*.vb”

   79         DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResults1

   80         DTE.Find.Action = vsFindAction.vsFindActionReplaceAll

   81         ‘If (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then

   82         ‘Throw New System.Exception(“vsFindResultNotFound”)

   83         ‘End If

   84         ‘DTE.Windows.Item(“{CF2DDC32-8CAD-11D2-9302-005345000000}”).Close()

   85 

   86     End Sub


Some ArcToolbox Commands

March 6, 2007

A few sample ArcToolbox commands I’ve been using recently. These can be run from the command line available in ArcCatalog (in the menu select Window >> Command Line).

workspace C:\MyPath\MyGeoDatabase.mdb
Intersect (featureClass1”;’featureDataset1\featureClass2′ ”) my_output_featureClass ALL # INPUT

This automates the Intersect tool, found in ArcToolbox under “Analysis Tools >> Overlay >> Intersect” and is useful if this is a process that has to be run several times, and can be part of a batch process. The first line sets the workspace to a geodatabase containing the two input classes, and the second line runs the intersect, outputting all fields to my_output_featureClass.

workspace C:\MyPath\MyGeoDatabase.mdb
delete myFeatureClass

This command deletes a feature class in the geodatabase.

More samples can be found in the ArcGIS Desktop Help under the “Geoprocessing tool reference” section.

commandline.png


Using ArcObjects to get Unique Values from a Table

February 25, 2007

A common requirement in many user dialogs is to display a list of unique values from a table, in order to delete or select records. While it is often easier to use Microsoft’s data objects, I try to use ArcObjects as the relevant libraries will always be installed on a user’s machine. The function will normally part of a larger project and I don’t like mixing the two methods to retrieve values from tables. I usually reuse a custom class for dealing with different types of geodatabases so that if a client changes from Access to SQL Server code changes will be minimal. Keeping up with changes to ESRI’s data access objects and Microsoft’s in the same project could get nasty..

There is a VBA / VB6 sample on how to get unique values at the EDN Site – but no .NET equivalent, hence my code sample below. There are no major changes except now a standard System.Collections enumerator is used rather than the ESRI IEnumVariantSimple.

    Public Sub ListUniqueRecords()

 

        Dim pMyTable As ITable

        Dim pCurs As ICursor = Nothing

        Dim intFieldIdx As Integer

        Dim pDataStatistics As IDataStatistics

        Dim pEnumVar As IEnumerator

        Dim pWorkspaceFactory As IWorkspaceFactory

        Dim pWorkspace As IWorkspace

        Dim pFeatWorkSpace As IFeatureWorkspace

        Dim strMyField As String = “VAL”

 

        Try

            pWorkspaceFactory = New AccessWorkspaceFactory

            pWorkspace = pWorkspaceFactory.OpenFromFile(“C:\MyPath\MyGDB.mdb”, 0)

            pFeatWorkSpace = CType(pWorkspace, IFeatureWorkspace)

            pMyTable = pFeatWorkSpace.OpenTable(“MyTableName”)

            intFieldIdx = pMyTable.FindField(strMyField)

 

            pCurs = pMyTable.Search(Nothing, True)

            pDataStatistics = New DataStatistics

            pDataStatistics.Field = strMyField

            pDataStatistics.Cursor = pCurs

 

            pEnumVar = CType(pDataStatistics.UniqueValues, IEnumerator)

 

            Do Until pEnumVar.MoveNext = False

                Debug.Print(pEnumVar.Current.ToString)

            Loop

 

        Catch ex As Exception

            Trace.WriteLine(ex.ToString)

        Finally

            ‘clean up

            pCurs = Nothing

            pWorkspace = Nothing

        End Try

 

    End Sub