Wednesday, January 26, 2011

Address Locator Style: Testing

Well since I don’t have ArcDesktop, and can’t do this.

I will do the easy thing and test my Address Locator Style by code, fun fun fun, NOT!

What do I want to achieve?

1. Basic: Create an Address Locator in a GeoDatabase

2. Optional: Delete an Address Locator in a GeoDatabase

3. Advanced: Use an Address Locator in a GeoDatabase

 

Well I decided to start from the ESRI example, that creates an Address Locator in a Personal GeoDatabase (after refactoring):

The main class:

  1. public class AddressLocatorUtils
  2. {
  3.     public void Create(string styleName, string mdbFilePath, string featureClassName, string locatorName)
  4.     {
  5.         var locatorStyle = LocatorWorkspaceUtils.Instance.GetLocatorStyle(styleName);
  6.         var dataset = AccessWorkspaceUtils.Instance.GetDataset(mdbFilePath, featureClassName);
  7.         LocatorWorkspaceUtils.Instance.SetLocatorDatasetName(dataset.FullName, locatorStyle);
  8.  
  9.         if (LocatorWorkspaceUtils.Instance.IsLocatorValidForSave(locatorStyle))
  10.         {
  11.             LocatorWorkspaceUtils.Instance.AddLocatorStyle(locatorName,mdbFilePath,(ILocator)locatorStyle);
  12.         }
  13.         else
  14.         {
  15.             throw new ApplicationException("Something went wrong...");
  16.         }
  17.     }
  18. }

The utils:

  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.         EsriInitilization.Start();
  18.         _locatorManager =
  19.             (ILocatorManager2)Activator.CreateInstance(Type.GetTypeFromProgID("esriLocation.LocatorManager"));
  20.     }
  21.  
  22.     public static LocatorWorkspaceUtils Instance
  23.     {
  24.         get { return instance; }
  25.     }
  26.  
  27.     #endregion
  28.  
  29.     public ILocatorStyle GetLocatorStyle(string styleName)
  30.     {
  31.         return GetLocatorStyle(styleName, "");
  32.     }
  33.  
  34.     public ILocatorStyle GetLocatorStyle(string styleName, string fileLocation)
  35.     {
  36.         var locatorWorkspace = GetLocatorWorkspace(fileLocation);
  37.  
  38.         // Get the locator style to base the new locator.
  39.         return locatorWorkspace.GetLocatorStyle(styleName);
  40.     }
  41.  
  42.     public void SetLocatorDatasetName(IName datasetName, ILocatorStyle locatorStyle)
  43.     {
  44.         var referenceDataTables = (IReferenceDataTables)locatorStyle;
  45.  
  46.         var enumReferenceDataTable = referenceDataTables.Tables;
  47.         enumReferenceDataTable.Reset();
  48.  
  49.         var referenceDataTableEdit = (IReferenceDataTableEdit)enumReferenceDataTable.Next();
  50.  
  51.         referenceDataTableEdit.Name_2 = (ITableName)datasetName;
  52.     }
  53.  
  54.     public bool IsLocatorValidForSave(ILocatorStyle locatorStyle)
  55.     {
  56.         return ((IReferenceDataTables)locatorStyle).HasEnoughInfo;
  57.     }
  58.  
  59.     public void AddLocatorStyle(string styleName, string fileLocation, ILocator locatorStyle)
  60.     {
  61.         var locatorWorkspace = GetLocatorWorkspace(fileLocation);
  62.         locatorWorkspace.AddLocator(styleName, locatorStyle, "", null);
  63.     }
  64.  
  65.     private ILocatorWorkspace GetLocatorWorkspace(string fileLocation)
  66.     {
  67.         return _locatorManager.GetLocatorWorkspaceFromPath(fileLocation);
  68.     }
  69. }

And AccessWorkspaceUtils contains:

  1. public IDataset GetDataset(string mdbFilePath, string featureClassName)
  2. {
  3.     // Open the feature class to use as reference data.
  4.     IWorkspaceFactory2 workspaceFactory2 = new AccessWorkspaceFactoryClass();
  5.     var featureWorkspace = (IFeatureWorkspace)workspaceFactory2.OpenFromFile(mdbFilePath, 0);
  6.  
  7.     // Set the feature class as the primary reference data table for the locator.
  8.     return (IDataset)featureWorkspace.OpenFeatureClass(featureClassName);
  9. }

My test:

  1. [TestMethod]
  2. public void Create_DllShepherdAddressLocator_AddressLocatorCreated()
  3. {
  4.     const string styleFileName = "Dll Shepherd Address";
  5.     const string referenceDataStyleName = "ZIP 5-Digit";
  6.     var styleName = String.Format("{0} - {1}", styleFileName,
  7.                                   referenceDataStyleName);
  8.     _addressLocatorUtils.Create(styleName, @"C:\Projects\Testings\Esri.AddressLocator\Esri.AddressLocator.CoreTests\bin\Debug\Israel_for_Roy.mdb",
  9.                                 "Streets", "New Dll Shepherd Locator");
  10. }

And it works, it create a style with the name.

Before:

Access-before-creating-address-locator

After:

access-after-creating-address-locator

Well we passed 1.

And then I tested it on something I created and it failed on:

  1. var referenceDataTables = (IReferenceDataTables)locatorStyle;

With this Exception:

System.InvalidCastException was unhandled by user code
  Message=Unable to cast COM object of type 'System.__ComObject' to interface type 'ESRI.ArcGIS.Location.IReferenceDataTables'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{600A5898-DDC1-11D3-9F74-00C04F8ED1C4}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

I decided that though ESRI help might be not very helpful here, their help in ArcCatalog might be better. So I asked Vered to test it and it just asked Vered if she wanted to send the Error to ESRI after closing her ArcCatalog – she almost killed me!

 

The exception is thrown simply because locatorStyle does not implements the IReferenceDataTables interface. Which is weird because locatorStyle can be converted to ESRIGen2AddressLocatorStyle:

Esri-location-model-diagram-locator-style

As you can see ESRIGen2AddressLocatorStyle inherits from ESRIReferenceDataAddressLocatorStyle which implements IReferenceDataTables. The weirdness doesn’t stop here looking at the source for ESRIGen2AddressLocatorStyle:

  1. namespace ESRI.ArcGIS.Location
  2. {
  3.     [CoClass(typeof (ESRIGen2AddressLocatorStyleClass))]
  4.     [Guid("655C5C62-6478-11D3-9F57-00C04F6BDF06")]
  5.     [ComImport]
  6.     public interface ESRIGen2AddressLocatorStyle : ILocator
  7.     {
  8.     }
  9. }

It only implements ILocator and I can’t even find ESRIReferenceDataAddressLocatorStyle, I started to think I can’t read this diagrams but:

ESRI-class-diagram-help

According to this both ESRIReferenceDataAddressLocatorStyle and ESRIGen2AddressLocatorStyle are classes and one inherits from the other – exactly like I thought!

 

 

TODO: add link in “after refactoring” to the post about refactoring ESRI part 1

Resources:

 

Keywords: ESRI, ArcObjects, Access, unit test, address locator