6

I am trying to emulate the Near (analysis) ArcGIS tool in ArcObjects. http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#//00080000001q000000

I want to use Mixed Feature Types. I have a point, polygon and line feature class and I want to use Near to compare all three of them with each other. This can be done one featureclass at a time like so (pseudocode logic):

  1. Create IFeatureIndex

  2. Store my feature class in IFeatureIndex.FeatureClass

  3. Use IIndexQuery.NearestFeature for every IGeometry in a feature class to get distance to the nearest feature

The problem I am running into is that I can only store one feature class in IFeatureIndex. I need to store three different feature classes (points, lines and polygons) to be able to properly execute my comparison. How can I do this? ESRI's feature that I linked above has managed to do this but there is unfortunately no way to peek at the code behind the geoprocessing tool that I know of.

Edit: I have solved this. See the answer below.

  • Don't forget to call ReleaseCOMObject on your cursors, it can really mess things up in this situation. – Kirk Kuykendall Jun 20 '13 at 22:47
  • @Aaron, he is the author :) User2036191: thank you for coming back and sharing your solution. It would work better if you move the solution you folded into the question into an answer, and then mark it correct. (self-answering is welcome and encouraged) – matt wilkie Jun 20 '13 at 23:10
  • I have moved my solution into its own answer. Thanks for the feedback, all. – Conor Jun 21 '13 at 3:04
4

enter image description here

You might consider using a spatial cache via ISpatialCacheManager3. The documentation doesn't say if all the featureclasses in the cache need to be in the same workspace, see the Using spatial caching to optimize spatial queries section. I'd be very curious if that is the case.

Here's a snippet from that page:

// Open the feature classes used by the queries.
IFeatureClass blocksFeatureClass = featureWorkspace.OpenFeatureClass("Blocks");
IFeatureClass parcelsFeatureClass = featureWorkspace.OpenFeatureClass("Parcels");

// Fill the spatial cache.
ISpatialCacheManager spatialCacheManager = (ISpatialCacheManager)featureWorkspace;

// Check if the cache has been filled.
if (!spatialCacheManager.CacheIsFull)
{
    // If not full, fill the cache.
    spatialCacheManager.FillCache(cacheExtent);
}

// Execute spatial queries.

// Empty the cache.
spatialCacheManager.EmptyCache();
  • I've put some more thought into this from your suggestion. It looks like I can create an IGeometryBag from the spatial query and then use IProximityOperator.ReturnDistance comparing each IGeometry in IGeometryBag to all other IGeometry features in the IGeometryBag. I could avoid using IFeatureIndex entirely through this methodology. If I get this working I'll post the code up here as thanks. – Conor Jun 19 '13 at 23:34
  • @user2036191 It simplifies things greatly if you can have a cutoff threshold. That way, for each feature, buffer it (or more quickly, by expanding its envelope) to create a search geometry. – Kirk Kuykendall Jun 20 '13 at 2:13
  • While I did not end up using ISpatialCacheManager3 to create my index, your post gave me ideas which led to my eventual solution. Thanks! – Conor Jun 20 '13 at 22:19
2

I solved this earlier today. Here is the code. A short pseudocode explanation follows.

        ESRI.ArcGIS.Geometry.IGeometryBag pGeometryBag = new GeometryBagClass();
        ESRI.ArcGIS.Geometry.IGeometryCollection pGeometryCollection = (IGeometryCollection)pGeometryBag;

        ESRI.ArcGIS.Carto.IEnumLayer pEnumLayer; //get all layers in the map
        pEnumLayer = pDoc.FocusMap.Layers;


        ESRI.ArcGIS.Carto.ILayer pLayer; //create a layer to store looped values of point combobox layers
        pLayer = pEnumLayer.Next();

        while (pLayer != null)//Get all the features and stick them into the geometry collection
        {

            ESRI.ArcGIS.Carto.IFeatureLayer pFeatureLayer = (IFeatureLayer)pLayer;
            if (pLayer == statusforselectedlayer) 
            {
                 ITable pTable = (ITable)pFeatureLayer.FeatureClass;
                 List<int> OIDList = new List<int>();
                 IQueryFilter queryFilter = new QueryFilterClass();
                 queryFilter.SubFields = pFeatureLayer.FeatureClass.OIDFieldName;
                 ICursor pCursor = pTable.Search(queryFilter, false);
                 IRow pRow = null;
                 while((pRow = pCursor.NextRow()) != null)
                 {
                     OIDList.Add(pRow.OID);
                 }

                 IFeatureCursor blocksCursor = pFeatureLayer.FeatureClass.GetFeatures(OIDList.ToArray(), false);
                 IFeature blockFeature = null;
                 while ((blockFeature = blocksCursor.NextFeature()) != null)
                 {
                     pGeometryCollection.AddGeometry(blockFeature.Shape);
                 }

            }

        ISpatialIndex spatialIndex = (ISpatialIndex)pGeometryBag;
        spatialIndex.AllowIndexing = true;
        spatialIndex.Invalidate();

        ISpatialFilter spatialFilter = new SpatialFilterClass();
        spatialFilter.Geometry = pGeometryBag;

        ESRI.ArcGIS.Geometry.IEnumGeometry pEnumGeometry = (IEnumGeometry)pGeometryBag;
        pEnumGeometry.Reset();
        ESRI.ArcGIS.Geometry.IGeometry pGeometry = pEnumGeometry.Next();

        List<double> distances = new List<double>();

        while (pGeometry != null)
        {
            IProximityOperator pProximity = (IProximityOperator)pGeometry;         

            ESRI.ArcGIS.Geometry.IEnumGeometry pEnumGeomBag = (IEnumGeometry)pGeometryBag;
            ESRI.ArcGIS.Geometry.IGeometry pGeomBagIterable = pEnumGeomBag.Next();

            while (pGeomBagIterable != null)
            {

                distances.Add(pProximity.ReturnDistance(pGeomBagIterable));
                pGeomBagIterable = pEnumGeomBag.Next();
            }
            pGeometry = pEnumGeometry.Next();


        }

The code loops through all layers in the map, takes all the features in a specified layer if it meets a certain criteria, and stores those features into an IGeometryBag. The code then loops through every IGeometry in the created IGeometryBag and compares each IGeometry to all other IGeometry in the IGeometryBag, getting the distance each time using IProximityOperator. These distances are stored in a list of type double named distances.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.