2

I want to get an exterior ring from a polygon. I need to itopologicaloperator.intersect a line with the polygon (exterior ring) to read attributes from the line.

I have the original Polygon I can get the ring. I have the lines. I have the itopologicaloperator.intersect

What I can't get is converting/casting the ring to a polygon so I can use itopologicaloperator.intersect.

What I have is islands inside of lakes, the attributes I read from the line are picking up the island features, but I only want the lake lines, (exterior) not the interior (islands)

for (intProgress = 0; intProgress <= intIndex - 1; intProgress++)
        {
            if (intExit == 1)
            {
                System.Environment.Exit(0);
            }

            // get the feature of the polygon class - idindex
            pPolyFeature = pPolyFeatureClass.GetFeature(IdIndex[intProgress]);
            intPolyOID = pPolyFeature.OID;
            pRelOp = (IRelationalOperator)pPolyFeature.Extent;
            dblFeatSize = Convert.ToDouble(pPolyFeature.get_Value(intPosPolyShapeArea));

            pGeometry = pPolyFeature.Shape;

            // http://resources.esri.com/help/9.3/arcgisengine/arcobjects/esriGeometry/IPolygon_Example.htm
            // set the polygon
            IPolygon4 pPolygon = pPolyFeature.Shape as IPolygon4;

            IGeometryCollection pPolyGeoCollection = (IGeometryCollection)pPolygon;

            // http://resources.arcgis.com/en/help/arcobjects-net/componenthelp/index.html#/ExteriorRingBag_Property/002m000002n0000000/
            // IPolygon4.ExteriorRingBag should be used instead of IPolygon.QueryExteriorRings
            // which does not work in .NET because of C-Style Arrays
            IGeometryBag pExtRingGeometryBag = pPolygon.ExteriorRingBag;

            // cast geometry to geometrycollection
            IGeometryCollection pExtRingGeometryCollection = pExtRingGeometryBag as IGeometryCollection;
            IFeatureLayer pRingLayer= null;

            int ExtRingGCCount = pExtRingGeometryCollection.GeometryCount;

            IGeometry pExtRingGeometry = null;

            for (int i = 0; i < pExtRingGeometryCollection.GeometryCount; i++)
            {
                pExtRingGeometry = pExtRingGeometryCollection.get_Geometry(i);

                pPolyGeoCollection = new Polygon() as IGeometryCollection;

                object obj = Type.Missing;
                pPolyGeoCollection.AddGeometry(pExtRingGeometry, ref obj, ref obj);
            }

            //Can now be used with ITopologicalOperator methods.
            // creates a new spatial filter
            // selects lines that share a boundary with a polygon
            pSpatialFilter = new SpatialFilter();
            // - same as above  still gives me the same id count as intInitSelectCount



            pTopoOp5 = (ITopologicalOperator5)??????????????????.Shape; // pExtRingGeometry..Shape;
            pTopoOp5.IsKnownSimple_2 = false;
            pTopoOp5.Simplify();

            // find lines whose interiors intersect the Boundary of the polygon
            SpatialFilter pSFPolygonIntersects = new SpatialFilterClass();

            pSFPolygonIntersects.GeometryField = pLineFeatureClass.ShapeFieldName;
            pSFPolygonIntersects.Geometry = pTopoOp5 as IGeometry;
            pSFPolygonIntersects.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;
            pSFPolygonIntersects.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;

            IFeatureCursor pFCCrossingLines = pLineFeatureClass.Search(pSFPolygonIntersects, false);
            IFeature pFeatureLineCross;

            List<int> LineOID = new List<int>();

            while ((pFeatureLineCross = pFCCrossingLines.NextFeature()) != null)
            {
                int featOID = pFeatureLineCross.OID;

                // This sample uses ITopologicalOperator::Intersect
                // to do the equivalent of a select by location with the share a line segment operator.

                // http://resources.esri.com/help/9.3/arcgisengine/arcobjects/esriGeometry/Share_a_line_segment.htm

                // is there an intersection between the two features
                // store the line 1 shape in the topo op
                ITopologicalOperator topologicalOperator = pFeatureLineCross.Shape as ITopologicalOperator;
                if (!topologicalOperator.IsSimple)
                {
                    topologicalOperator.Simplify();
                }
2

Polygons are made from rings. I think you want to take the exterior ring from a polygon and make a new polygon from just the exterior:

// earlier in the code..
pGeometry = pPolyFeature.Shape; // don't do this, it can have side effects
pGeometry = pPolyFeature.ShapeCopy; // do this.

// you've got this mostly right
IGeometryCollection pPolyGeoCollection = new PolygonClass();
pPolyGeoCollection.AddGeometry(pExtRingGeometry, ref obj, ref obj);

// now make the geometry collection into a geometry
IGeometry pReturnGeometry = pPolyGeoCollection as IGeometry;
pReturnGeometry.SpatialReference = pGeometry.SpatialReference; // copy spatial reference

It's a bit confusing, an IGeometryCollection is made from a group of IGeometry objects but an IGeometryCollection can be made into a single IGeometry, and then theoretically it can also be added to another IGeometryCollection and thence made into an IGeometry.... like a dog chasing its tail.

In this section here:

for (int i = 0; i < pExtRingGeometryCollection.GeometryCount; i++)
   {
      pExtRingGeometry = pExtRingGeometryCollection.get_Geometry(i);

      pPolyGeoCollection = new Polygon() as IGeometryCollection;

      object obj = Type.Missing;
      pPolyGeoCollection.AddGeometry(pExtRingGeometry, ref obj, ref obj);
   }

You are making a new polygon every time, so in the end there will be only one ring in the polygon which will be the last ring in the bag.

I think you are wanting to do it this way:

object obj = Type.Missing;
IGeometryCollection pPolyGeoCollection = new PolygonClass();
for (int i = 0; i < pExtRingGeometryCollection.GeometryCount; i++)
{
    pExtRingGeometry = pExtRingGeometryCollection.get_Geometry(i);
    pPolyGeoCollection.AddGeometry(pExtRingGeometry, ref obj, ref obj);
}
IGeometry pReturnGeometry = pPolyGeoCollection as IGeometry;
pReturnGeometry.SpatialReference = pGeometry.SpatialReference;
// now it can be used with ITopologicalOperator
pTopoOp5 = (ITopologicalOperator5)pReturnGeometry;

To put all the exterior rings into a multi-part polygon.

  • Thanks Micheal. Appreciate the help. Now I get an invalid cast exception on pTopoOp5 = (ITopologicalOperator5)pReturnGeometry – Deke Jun 20 '14 at 11:33
  • Hi Deke, only polylines implement ITopologicalOperator5, use ITopologicalOperator4 instead. That works on polylines and polygons but not multipoints or geometrybags. – Michael Stimson Jun 21 '14 at 3:22
  • Thanks again. I got the code to work I will post it when I get a chance. Can you look at it? Sugest room for improvements? – Deke Jun 21 '14 at 12:05
  • I'll have a look at it, no guarantees though. – Michael Stimson Jun 21 '14 at 12:27
1

Full Code.

Basically the code loops through polygons, selects the exterior ring, copies the OID of a line feature class to a list that intersects the exterior rings. then loops through a line feature class looking for feature codes then based on the feature code that has the longest accumulated segment length, puts the feature code attribute into the polygon row.

for (intProgress = 0; intProgress <= intIndex - 1; intProgress++) { if (intExit == 1) { System.Environment.Exit(0); }

 // get the feature of the polygon class - idindex
 pPolyFeature = pPolyFeatureClass.GetFeature(IdIndex[intProgress]);
 intPolyOID = pPolyFeature.OID;
 pRelOp = (IRelationalOperator)pPolyFeature.Extent;
 dblFeatSize = Convert.ToDouble(pPolyFeature.get_Value(intPosPolyShapeArea));

 pGeometry = pPolyFeature.ShapeCopy; // changed to shapecopy from shape

 // http://resources.esri.com/help/9.3/arcgisengine/arcobjects/esriGeometry/IPolygon_Example.htm
 // set the polygon
 IPolygon4 pPolygon = pPolyFeature.Shape as IPolygon4;

 // http://resources.arcgis.com/en/help/arcobjects-net/componenthelp/index.html#/ExteriorRingBag_Property/002m000002n0000000/
 // IPolygon4.ExteriorRingBag should be used instead of IPolygon.QueryExteriorRings
 // which does not work in .NET because of C-Style Arrays

 IGeometryBag pExtRingGeometryBag = pPolygon.ExteriorRingBag;

 // cast geometrybag to geometrycollection
 IGeometryCollection pExtRingGeometryCollection = pExtRingGeometryBag as IGeometryCollection;

 IGeometry extRingGeometry;
 ISegmentCollection pRingSegCollection = null;

 // http://gis.stackexchange.com/questions/50322/is-ipolygon2-queryexteriorringsex-method-not-working-in-arcgis-version-10-1-sp1

 for (int i = 0; i < pExtRingGeometryCollection.GeometryCount; i++)
 {
       extRingGeometry = pExtRingGeometryCollection.get_Geometry(i);
       pRingSegCollection = (ISegmentCollection)extRingGeometry;
 }

 IPolyline polyline = (IPolyline)new PolylineClass();
 IGeometryCollection geomcoll = (IGeometryCollection)polyline;
 ISegmentCollection pathcoll = (ISegmentCollection)new PathClass();

 for (int i = 0; i < pRingSegCollection.SegmentCount; i++)
 {
       var lineSeg = pRingSegCollection.get_Segment(i);
       pathcoll.AddSegment(lineSeg);
 }

 geomcoll.AddGeometry((IGeometry)pathcoll);
 geomcoll.GeometriesChanged();

 //Can now be used with ITopologicalOperator methods.

 // - same as above  still gives me the same id count as intInitSelectCount
 pTopoOp4 = (ITopologicalOperator4) geomcoll;
 pTopoOp4.IsKnownSimple_2 = false;
 pTopoOp4.Simplify();

 // find lines whose interiors intersect the Boundary of the polygon
 SpatialFilter pSFPolygonIntersects = new SpatialFilterClass();

 pSFPolygonIntersects.GeometryField = pLineFeatureClass.ShapeFieldName;
 pSFPolygonIntersects.Geometry = geomcoll as IGeometry; // was pGeometry
 pSFPolygonIntersects.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;
 pSFPolygonIntersects.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;

 IFeatureCursor pFCCrossingLines = pLineFeatureClass.Search(pSFPolygonIntersects, false);

 IFeature pFeatureLineCross;

 List<int> LineOID = new List<int>();

 while ((pFeatureLineCross = pFCCrossingLines.NextFeature()) != null)
 {
      int featOID = pFeatureLineCross.OID;

      // This sample uses ITopologicalOperator::Intersect
      // to do the equivalent of a select by location with the share a line segment operator.

      // http://resources.esri.com/help/9.3/arcgisengine/arcobjects/esriGeometry/Share_a_line_segment.htm

      // is there an intersection between the two features
      // store the line 1 shape in the topo op
      ITopologicalOperator topologicalOperator = pFeatureLineCross.Shape as ITopologicalOperator;
      if (!topologicalOperator.IsSimple)
      {
            topologicalOperator.Simplify();
      }

      LineOID.Add(featOID);
 }

 // writes to output file
 outStream.WriteLine(string.Format("{0,6:G} of {1:G} ObjectId: {2,6:G}", intProgress + 1, intIndex, intPolyOID) + string.Format(" InitSelect: {0,4:G} Features: {1,4:G} Size: {2:#.##}", intInitSelectCount, LineOID.Count, dblFeatSize));
 outStream.Flush();

 foreach (int id in LineOID)
 {
      Application.DoEvents();

      if (intExit == 1)
      {
            System.Environment.Exit(0);
      }

      // gets the line feature from the feature clas and assigns it as a feature
      pLineFeature = pLineFeatureClass.GetFeature(id); // line 797 in converted text

      // if the extent of the line is the same as the polygon,
      //no need to check the other lines
      if (pRelOp.Equals(pLineFeature.Extent))
      {
            objLineFeat = pLineFeature.get_Value(intPosLineFeatCode);
            sLineNameId_1 = pLineFeature.get_Value(intPosLineNameId_1).ToString();
            sLineName_1 = pLineFeature.get_Value(intPosLineName_1).ToString();

            sSource = sLineNameId_1;

            htFeatCode.Clear();
            htFeatCode.Add(objLineFeat, 1);

            htSource.Clear();
            htSource.Add(sSource, 1);

            objLineFeat = null;
            sSource = "";
            pLineFeature = null;
            GC.Collect();
            break;
      }

      pPolyLine = (IPolyline)pLineFeature.Shape;
      dblFeatLength = pPolyLine.Length;

      objLineFeat = pLineFeature.get_Value(intPosLineFeatCode);
      sLineNameId_1 = pLineFeature.get_Value(intPosLineNameId_1).ToString();
      sLineName_1 = pLineFeature.get_Value(intPosLineName_1).ToString();
      sSource = sLineNameId_1;

      // gets length of line feature code
      intLength = objLineFeat.ToString().Length;

      // converts objlinefeat value to string
      string strKeyCodeText = objLineFeat.ToString();

      // assign second last character to a string
      strKeyCode = (strKeyCodeText.Substring(strKeyCodeText.Length - 2, 1));

      // if strkeycode does not equal 59 or N
      // 59 is a spine
      // n is a neatline
      if (strKeyCode != "59" && strKeyCode != "N")
      {
           // if featcode = wabafo
           // dont add feat code to hash table
           if (strKeyCodeText != "WABAF0")
           {
                // if hash table feature code 
                if (htFeatCode.ContainsKey(objLineFeat))
                {
                     dblTotalLength = (double)htFeatCode[objLineFeat];
                     htFeatCode.Remove(objLineFeat);
                     htFeatCode.Add(objLineFeat, dblTotalLength + dblFeatLength);
                }
                else
                {
                     htFeatCode.Add(objLineFeat, dblFeatLength);
                }
           }
      }

      if (htSource.ContainsKey(sSource))
      {
           intSource = (int)htSource[sSource];
           htSource.Remove(sSource);
           htSource.Add(sSource, intSource + 1);
      }
      else
      {
           htSource.Add(sSource, 1);
      }

      if (sLineNameId_1 != "")
      {
           if (htName.ContainsKey(sLineNameId_1))
           {
                double dbTotalNameLength = (double)htName[sLineNameId_1];
                htName.Remove(sLineNameId_1);
                htName.Add(sLineNameId_1, dbTotalNameLength + dblFeatLength);
           }
           else
           {
                 htName.Add(sLineNameId_1, dblFeatLength);
           }
      }

      objLineFeat = null;
      dblFeatLength = 0;
      sSource = "";
      intSource = 0;
      pPolyLine = null;
      pLineFeature = null;
      GC.Collect();
 }

 pRelOp = null;
 intSource = 0;

 // get the current row (OID) from the polytable
 pRow = pPolyTable.GetRow(IdIndex[intProgress]); // ?????????????????????

 // get the Value of the polygon feature code and pass it to FindMostFeat
 pRow.set_Value(intPosPolyFeatCode, FindMostFeat(ref htFeatCode));
 pRow.set_Value(intPosPolyFeatCode, (pRow.get_Value(intPosPolyFeatCode).ToString()));
 pRow.set_Value(intPosPolyHID, null);
 pRow.set_Value(intPosPolyZValue, "0");
 pRow.set_Value(intPosPolyStartDate, DateTime.Today);
 pRow.set_Value(intPosPolyProduct, "GEN");
 pRow.set_Value(intPosPolyScale, "G");
 pRow.set_Value(intPosPolyCollector, "GC");
 pRow.set_Value(intPosPolyCapture, "O");
 pRow.set_Value(intPosPolyProdYear, "ZZ");
 pRow.set_Value(intPosPolyProdMonth, "Z");
 pRow.set_Value(intPosPolyX_Y_Acc, "E");
 pRow.set_Value(intPosPolyZ_Acc, "E");
 pRow.set_Value(intPosPolyArea, dblFeatSize);
 pRow.set_Value(intPosPolyMinZ, null);
 pRow.set_Value(intPosPolyMaxZ, null);
 pRow.set_Value(intPosPolyNameId_1, ""); // objHighestName);
 pRow.set_Value(intPosPolyName_1, ""); // ReturnNameValue);
 pRow.set_Value(intPosPolyPolyClass, GetPolyClass(pRow.get_Value(intPosPolyFeatCode).ToString()));

 pRow.Store();

 htFeatCode.Clear();
 htSource.Clear();
 htName.Clear();

 TotalTime = TotalTime + DateTime.Now.Subtract(StartTime);
 pPolyFeature = null;
 pGeometry = null;
 pLineFeatureLayer = null;
 pSpatialFilter = null;
 pEnumIDs = null;
 ReturnNameValue = null;
 objHighestName = null;
 objNameCode = null;

 GC.Collect();
 }

}

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.