Tuesday, March 6, 2012

Refactoring ESRI: Making the Examples Readable, Part 1

This is the refactor for ESRI example for creating an Address Locator in a Personal GeoDatabase:

  1. public void AddressLocatorPersonalGDBViaArcObjects()
  2. {
  3.     // Open the default local locator workspace to get the locator style.
  4.     System.Object obj = Activator.CreateInstance(Type.GetTypeFromProgID(
  5.         "esriLocation.LocatorManager"));
  6.     ILocatorManager2 locatorManager2 = obj as ILocatorManager2;
  7.     ILocatorWorkspace locatorWorkspace = locatorManager2.GetLocatorWorkspaceFromPath
  8.         ("");
  9.  
  10.     // Get the locator style to base the new locator.
  11.     ILocatorStyle locatorStyle = locatorWorkspace.GetLocatorStyle(
  12.         "US Address - Dual Ranges");
  13.  
  14.     // Open the feature class to use as reference data.
  15.     IWorkspaceFactory2 workspaceFactory2 = new AccessWorkspaceFactoryClass();
  16.     IWorkspace workspace = workspaceFactory2.OpenFromFile(
  17.         @"D:\workspace\arcobjects\location\redlands.mdb",
  18.         0);
  19.     IFeatureWorkspace featureWorkspace = (IFeatureWorkspace) workspace;
  20.     IFeatureClass featureClass = featureWorkspace.OpenFeatureClass("Streets");
  21.  
  22.     // Set the feature class as the primary reference data table for the locator.
  23.     IDataset dataset = (IDataset) featureClass;
  24.  
  25.     IReferenceDataTables referenceDataTables = (IReferenceDataTables) locatorStyle;
  26.     IEnumReferenceDataTable enumReferenceDataTable = referenceDataTables.Tables;
  27.     enumReferenceDataTable.Reset();
  28.  
  29.     IReferenceDataTable referenceDataTable = enumReferenceDataTable.Next();
  30.     IReferenceDataTableEdit referenceDataTableEdit = (IReferenceDataTableEdit)
  31.                                                      referenceDataTable;
  32.     IName name = dataset.FullName;
  33.     referenceDataTableEdit.Name_2 = (ITableName) name;
  34.  
  35.     // Store the new locator in the same workspace as the reference data.
  36.     if (referenceDataTables.HasEnoughInfo)
  37.     {
  38.         locatorWorkspace = locatorManager2.GetLocatorWorkspaceFromPath(
  39.             @"D:\workspace\arcobjects\location\redlands.mdb");
  40.         ILocator locator = locatorWorkspace.AddLocator("New Redlands Locator",
  41.                                                        (ILocator) locatorStyle, "", null);
  42.     }
  43. }

The first order of business is refactoring this method.

The first thing to go was this:

  1. IFeatureClass featureClass = featureWorkspace.OpenFeatureClass("Streets");
  2.  
  3. // Set the feature class as the primary reference data table for the locator.
  4. IDataset dataset = (IDataset) featureClass;

To:

  1. IDataset dataset = (IDataset)featureWorkspace.OpenFeatureClass("Streets");

There was a few other things like: referenceDataTable->referenceDataTableEdit,

The end result was:

  1. public void AddressLocatorPersonalGDBViaArcObjects()
  2. {
  3.     // Open the default local locator workspace to get the locator style.
  4.     System.Object obj = Activator.CreateInstance(Type.GetTypeFromProgID(
  5.         "esriLocation.LocatorManager"));
  6.     var locatorManager2 = obj as ILocatorManager2;
  7.     var locatorWorkspace = locatorManager2.GetLocatorWorkspaceFromPath
  8.         ("");
  9.  
  10.     // Get the locator style to base the new locator.
  11.     var locatorStyle = locatorWorkspace.GetLocatorStyle(
  12.         "US Address - Dual Ranges");
  13.     // Open the feature class to use as reference data.
  14.     IWorkspaceFactory2 workspaceFactory2 = new AccessWorkspaceFactoryClass();
  15.     var featureWorkspace = (IFeatureWorkspace)workspaceFactory2.OpenFromFile(
  16.         @"D:\workspace\arcobjects\location\redlands.mdb",
  17.         0);
  18.  
  19.     // Set the feature class as the primary reference data table for the locator.
  20.     var dataset = (IDataset)featureWorkspace.OpenFeatureClass("Streets");
  21.     
  22.     var referenceDataTables = (IReferenceDataTables)locatorStyle;
  23.  
  24.     var enumReferenceDataTable = referenceDataTables.Tables;
  25.     enumReferenceDataTable.Reset();
  26.  
  27.     var referenceDataTableEdit = (IReferenceDataTableEdit) enumReferenceDataTable.Next();
  28.     
  29.     referenceDataTableEdit.Name_2 = (ITableName)dataset.FullName;
  30.  
  31.     // Store the new locator in the same workspace as the reference data.
  32.     if (referenceDataTables.HasEnoughInfo)
  33.     {
  34.         locatorWorkspace = locatorManager2.GetLocatorWorkspaceFromPath(
  35.             @"D:\workspace\arcobjects\location\redlands.mdb");
  36.         locatorWorkspace.AddLocator("New Redlands Locator",
  37.                                                        (ILocator) locatorStyle, "", null);
  38.     }
  39. }

39 lines without any loss of readability, not that it says much – it still not readable!

The next thing to look at is creating the ILocatorManager2, I don’t know about you but I prefer getting an exception about converting than about Null reference:

  1. (ILocatorManager2)Activator.CreateInstance(Type.GetTypeFromProgID("esriLocation.LocatorManager"));

(that also solved the problem of the ‘obj’ variable naming)

The next part is creating a Utils class for LocatorWorkspace:

  1. public class LocatorWorkspaceUtils
  2. {
  3.     private readonly ILocatorManager2 _locatorManager;
  4.  
  5.     #region Singleton
  6.  
  7.     private static readonly LocatorWorkspaceUtils instance = new LocatorWorkspaceUtils();
  8.  
  9.     // Explicit static constructor to tell C# compiler
  10.     // not to mark type as beforefieldinit
  11.     static LocatorWorkspaceUtils()
  12.     {
  13.     }
  14.  
  15.     private LocatorWorkspaceUtils()
  16.     {
  17.         _locatorManager =
  18.             (ILocatorManager2)Activator.CreateInstance(Type.GetTypeFromProgID("esriLocation.LocatorManager"));
  19.     }
  20.  
  21.     public static LocatorWorkspaceUtils Instance
  22.     {
  23.         get { return instance; }
  24.     }
  25.  
  26.     #endregion

It’s singleton with ILocatorManager already set, now the only thing we use it for is:

  1. private ILocatorWorkspace GetLocatorWorkspace(string fileLocation)
  2. {
  3.     return _locatorManager.GetLocatorWorkspaceFromPath(fileLocation);
  4. }

Getting the style:

  1. public ILocatorStyle GetLocatorStyle(string styleName)
  2. {
  3.     return GetLocatorStyle(styleName, "");
  4. }
  5.  
  6. public ILocatorStyle GetLocatorStyle(string styleName, string fileLocation)
  7. {
  8.     var locatorWorkspace = GetLocatorWorkspace(fileLocation);
  9.  
  10.     // Get the locator style to base the new locator.
  11.     return locatorWorkspace.GetLocatorStyle(styleName);
  12. }

Adding the locator style:

  1. public void AddLocatorStyle(string styleName, string fileLocation, ILocator locatorStyle)
  2. {
  3.     var locatorWorkspace = GetLocatorWorkspace(fileLocation);
  4.     locatorWorkspace.AddLocator(styleName, locatorStyle, "", null);
  5. }

Setting the dataset name in the locator:

  1. public void SetLocatorDatasetName(IName datasetName, ILocatorStyle locatorStyle)
  2. {
  3.     var referenceDataTables = (IReferenceDataTables)locatorStyle;
  4.  
  5.     var enumReferenceDataTable = referenceDataTables.Tables;
  6.     enumReferenceDataTable.Reset();
  7.  
  8.     var referenceDataTableEdit = (IReferenceDataTableEdit)enumReferenceDataTable.Next();
  9.  
  10.     //var name = new TableNameClass {Name = datasetName};
  11.  
  12.     referenceDataTableEdit.Name_2 = (ITableName)datasetName;
  13. }

Checking if the locator is good to be saved:

  1. public bool IsLocatorValidForSave(ILocatorStyle locatorStyle)
  2. {
  3.     return ((IReferenceDataTables)locatorStyle).HasEnoughInfo;
  4. }

Now the code looks like:

  1. public void AddressLocatorPersonalGDBViaArcObjects()
  2. {
  3.     // Get the locator style to base the new locator.
  4.     var locatorStyle = LocatorWorkspaceUtils.Instance.GetLocatorStyle("US Address - Dual Ranges");
  5.  
  6.     // Open the feature class to use as reference data.
  7.     IWorkspaceFactory2 workspaceFactory2 = new AccessWorkspaceFactoryClass();
  8.     var featureWorkspace = (IFeatureWorkspace)workspaceFactory2.OpenFromFile(
  9.         @"D:\workspace\arcobjects\location\redlands.mdb",
  10.         0);
  11.  
  12.     // Set the feature class as the primary reference data table for the locator.
  13.     var dataset = (IDataset)featureWorkspace.OpenFeatureClass("Streets");
  14.     LocatorWorkspaceUtils.Instance.SetLocatorDatasetName(dataset.FullName, locatorStyle);
  15.  
  16.     if (LocatorWorkspaceUtils.Instance.IsLocatorValidForSave(locatorStyle))
  17.     {
  18.         LocatorWorkspaceUtils.Instance.AddLocatorStyle("New Redlands Locator",
  19.             @"D:\workspace\arcobjects\location\redlands.mdb",
  20.             (ILocator)locatorStyle);
  21.     }
  22. }

Now something seems a bit off here, the only reason we open the workspace for the MDB file is to get the dataset FullName, WTF?!? Why not just pass the name as a… wait for it… string?

TODO: continue this

 

Keywords: ESRI, ArcObjects, refactor

IceRocket Tags: ,,