public List<RelatedCurve> GetTangentCurveMatchFeatures(IFeatureClass FeatureClass, IFeature inFeature, IPolycurve inPolycurve, string WhereClause, int idxRadius, int idxCenterPointID, double SegementLength, out List<RelatedLine> tangentLines) //double AngleToleranceTangentCompareInDegrees, double StraightLinesBreakLessThanInDegrees, //double MaximumDeltaInDegrees, double ExcludeTangentsShorterThan, // out int outFoundTangentCurvesCount, ref List<RelatedCurve> CurveInfoFromNeighbours) { List<RelatedCurve> CurveInfoFromNeighbours = new List<RelatedCurve>(); tangentLines = new List<RelatedLine>(); ILine pOriginalChord = (ILine)new Line(); pOriginalChord.PutCoords(inPolycurve.FromPoint, inPolycurve.ToPoint); IVector3D vecOriginalSelected = (IVector3D)new Vector3D(); vecOriginalSelected.PolarSet(pOriginalChord.Angle, 0, 1); IGeometryBag pGeomBag = (IGeometryBag)new GeometryBag(); IGeometryCollection pGeomColl = (IGeometryCollection)pGeomBag; IGeometry MultiPartPolyLine = (IGeometry)new Polyline(); //qi IGeoDataset pGeoDS = (IGeoDataset)FeatureClass; ISpatialReference spatialRef = pGeoDS.SpatialReference; MultiPartPolyLine.SpatialReference = spatialRef; IGeometryCollection geometryCollection2 = MultiPartPolyLine as IGeometryCollection; ILine inGeomTangentLineAtEnd = new Line(); //new ILine inGeomTangentLineAtStart = new Line(); //new object objMissing = Type.Missing; for (int i = 0; i < 2; i++) { ILine pThisLine = null; if (i == 0) { //Add line tangent at end point inPolycurve.QueryTangent(esriSegmentExtension.esriExtendAtTo, 1.0, true, CurveByInferenceSettings.Instance.TangentQueryLength, inGeomTangentLineAtEnd); pThisLine = new Line(); pThisLine.PutCoords(inGeomTangentLineAtEnd.FromPoint, inGeomTangentLineAtEnd.ToPoint); pGeomColl.AddGeometry(pThisLine); } else { //Add line tangent at start point inPolycurve.QueryTangent(esriSegmentExtension.esriExtendAtFrom, 0.0, true, CurveByInferenceSettings.Instance.TangentQueryLength, inGeomTangentLineAtStart); pThisLine = new Line(); pThisLine.PutCoords(inGeomTangentLineAtStart.FromPoint, inGeomTangentLineAtStart.ToPoint); pGeomColl.AddGeometry(pThisLine); } //Create a new path for each line. ISegmentCollection newPath = (ISegmentCollection)new Path(); newPath.AddSegment((ISegment)pThisLine, ref objMissing, ref objMissing); //The spatial reference associated with geometryCollection will be assigned to all incoming paths and segments. geometryCollection2.AddGeometry(newPath as IGeometry, ref objMissing, ref objMissing); } //now buffer the tangent lines IGeometryCollection outBufferedGeometryCol = (IGeometryCollection)new GeometryBag(); for (int jj = 0; jj < geometryCollection2.GeometryCount; jj++) { IPath pPath = geometryCollection2.get_Geometry(jj) as IPath; IGeometryCollection pPolyL = (IGeometryCollection)new Polyline(); pPolyL.AddGeometry((IGeometry)pPath); ITopologicalOperator topologicalOperator = (ITopologicalOperator)pPolyL; IPolygon pBuffer = topologicalOperator.Buffer(CurveByInferenceSettings.Instance.TangentQueryBuffer) as IPolygon; outBufferedGeometryCol.AddGeometry(pBuffer, ref objMissing, ref objMissing); } ITopologicalOperator pUnionedBuffers = null; pUnionedBuffers = new Polygon() as ITopologicalOperator; pUnionedBuffers.ConstructUnion((IEnumGeometry)outBufferedGeometryCol); ISpatialFilter pSpatFilt = new SpatialFilter(); pSpatFilt.WhereClause = WhereClause; pSpatFilt.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects; pSpatFilt.SearchOrder = esriSearchOrder.esriSearchOrderSpatial; pSpatFilt.Geometry = (IGeometry)pUnionedBuffers; IFeatureCursor pFeatCursLines = null; try { pFeatCursLines = FeatureClass.Search(pSpatFilt, false); } catch (Exception ex) { messageBox.Show(ex.Message); return CurveInfoFromNeighbours; } IVector3D foundGeomVector = (IVector3D)new Vector3D(); IFeature foundFeature = null; bool bHasTangentStraightLineAtJunction = false; //A list of relative orientation values that have large breaks List<RelativeOrientation> lstLargeBreak = new List<RelativeOrientation>(); while ((foundFeature = pFeatCursLines.NextFeature()) != null) { if (inFeature.OID == foundFeature.OID) { Marshal.ReleaseComObject(foundFeature); continue; } IGeometry foundLineGeom = foundFeature.ShapeCopy; IPolyline foundPolyLine = foundLineGeom as IPolyline4; IPolycurve foundPolyCurve = foundLineGeom as IPolycurve; RelativeOrientation iRelativeOrientation = GetRelativeOrientation(foundPolyCurve, inPolycurve); //iRelativeOrientation == 1 --> closest points are original TO and found TO //iRelativeOrientation == 2 --> closest points are original TO and found FROM //iRelativeOrientation == 3 --> closest points are original FROM and found TO //iRelativeOrientation == 4 --> closest points are original FROM and found FROM double foundRadius = (foundFeature.get_Value(idxRadius) is DBNull) ? 0 : (double)foundFeature.get_Value(idxRadius); int? foundCentriodID = (foundFeature.get_Value(idxCenterPointID) is DBNull) ? null : (int?)foundFeature.get_Value(idxCenterPointID); //if the found feature has the same start and endpoints, assume that the feature is the same. If that feature has radius and centerpoint information //assume that feature is unchanged and update the current feature with the found feature. if (iRelativeOrientation == RelativeOrientation.Same || iRelativeOrientation == RelativeOrientation.Reverse) { if (foundRadius > 0 && foundCentriodID.HasValue) { //Curved line double adjustedRadius = iRelativeOrientation == RelativeOrientation.Same ? foundRadius : -1 * foundRadius; CurveInfoFromNeighbours.Clear(); CurveInfoFromNeighbours.Add(new RelatedCurve(foundFeature.OID, adjustedRadius, foundCentriodID.Value, iRelativeOrientation)); Marshal.ReleaseComObject(foundFeature); Marshal.FinalReleaseComObject(pFeatCursLines); return CurveInfoFromNeighbours; } else if (foundPolyLine.Length > inPolycurve.Length * CurveByInferenceSettings.Instance.TangentOverlapScaleFactor) { //straight line CurveInfoFromNeighbours.Clear(); Marshal.ReleaseComObject(foundFeature); Marshal.FinalReleaseComObject(pFeatCursLines); return CurveInfoFromNeighbours; } Marshal.ReleaseComObject(foundFeature); continue; } /****************************** * * Compare chord of the in geometry vs the found geometry * * ****************************/ ILine foundChord = new Line(); foundChord.PutCoords(foundPolyCurve.FromPoint, foundPolyCurve.ToPoint); //Not sure about this, but it looks like this may be sensitive to the order in which the records are returned //first check for likelihood that subject line is supposed to stay straight, by geometry chord bearing angle break test //compare the in geometries chord with the found geometries chord foundGeomVector.PolarSet(foundChord.Angle, 0, 1); double chordsAngleDelta = Math.Abs(Math.Acos(foundGeomVector.DotProduct(vecOriginalSelected)) * 180 / Math.PI); //in degrees //large angle break non-tangent, greater than 3 degrees if (chordsAngleDelta > CurveByInferenceSettings.Instance.dLargeAngleBreakInDegrees && chordsAngleDelta < (180 - CurveByInferenceSettings.Instance.dLargeAngleBreakInDegrees)) { if (!lstLargeBreak.Contains(iRelativeOrientation)) { lstLargeBreak.Add(iRelativeOrientation); //check to see if there is already a related curve for the current relative orientation //if(CurveInfoFromNeighbours.Any(w=>w.Orientation == iRelativeOrientation)) //{ // bHasTangentStraightLineAtJunction = true; //} } } double ExcludeTangentsShorterThan = SegementLength * CurveByInferenceSettings.Instance.ExcludeTangentsShorterThanScaler; /* if(foundPolyCurve.Length < ExcludeTangentsShorterThan) { //exclude short segements Marshal.ReleaseComObject(foundFeature); continue; } else if (!foundCentriodID.HasValue && foundRadius == 0) { //attribute straight line, check the geometry to see if it is tangent at the endpoint if (chordsAngleDelta <= CurveByInferenceSettings.Instance.StraightLinesBreakLessThanInDegrees || (180 - chordsAngleDelta) < CurveByInferenceSettings.Instance.StraightLinesBreakLessThanInDegrees) { if (lstLargeBreak.Contains(iRelativeOrientation)) bHasTangentStraightLineAtJunction = true; } } else if (foundCentriodID.HasValue && foundRadius != 0) { //attribute curved line if ((inPolycurve.Length / foundRadius * 180 / Math.PI) > CurveByInferenceSettings.Instance.MaximumDeltaInDegrees) { //if the resulting curve ('in feature' length with 'found feature' radius) would have a central angle more than MaximumDeltaInDegrees degrees then skip Marshal.ReleaseComObject(foundFeature); continue; } } else { //invalid state, curves should have both centriod and radius, straigh lines should have niether. Marshal.ReleaseComObject(foundFeature); throw new Exception(string.Format("Invalid attribute state")); } */ if ((chordsAngleDelta <= CurveByInferenceSettings.Instance.StraightLinesBreakLessThanInDegrees || (180 - chordsAngleDelta) < CurveByInferenceSettings.Instance.StraightLinesBreakLessThanInDegrees) && !foundCentriodID.HasValue && foundRadius == 0 && !(foundPolyCurve.Length < ExcludeTangentsShorterThan)) { if (lstLargeBreak.Contains(iRelativeOrientation)) bHasTangentStraightLineAtJunction = true; } if (!foundCentriodID.HasValue || foundRadius == 0 || foundPolyCurve.Length < ExcludeTangentsShorterThan) { //staight line if (foundPolyCurve.Length > ExcludeTangentsShorterThan) { //vecOriginalSelected //ISegmentCollection collection = (ISegmentCollection)polyline; //ISegment segement = collection.get_Segment(collection.SegmentCount - 1); ILine line = new Line(); double Angle = 0.0; switch (iRelativeOrientation) { case RelativeOrientation.To_From: foundPolyLine.QueryTangent(esriSegmentExtension.esriExtendAtFrom, 0.0, true, 1.0, line); Angle = line.Angle; break; case RelativeOrientation.From_From: foundPolyLine.QueryTangent(esriSegmentExtension.esriExtendAtFrom, 0.0, true, 1.0, line); Angle = (line.Angle > 0) ? line.Angle - Math.PI : line.Angle + Math.PI; break; case RelativeOrientation.To_To: foundPolyLine.QueryTangent(esriSegmentExtension.esriExtendAtTo, 1.0, true, 1.0, line); Angle = (line.Angle > 0) ? line.Angle - Math.PI : line.Angle + Math.PI; break; case RelativeOrientation.From_To: foundPolyLine.QueryTangent(esriSegmentExtension.esriExtendAtTo, 1.0, true, 1.0, line); Angle = line.Angle; break; } IVector3D foundVector = new Vector3D() as IVector3D; foundVector.PolarSet(Angle, 0, 1); double dVectDiff = Math.Acos(vecOriginalSelected.DotProduct(foundVector)); //Console.WriteLine("Add Straight Line: {0}, {1} -> {2}", foundFeature.OID, line.Angle, ((ILine)segement).Angle); tangentLines.Add(new RelatedLine(foundFeature.OID, toDegrees(Angle), toDegrees(dVectDiff), iRelativeOrientation)); } //if the feature has a null centrpointID then skip. Marshal.ReleaseComObject(foundFeature); continue; } if (Math.Abs(inPolycurve.Length / foundRadius * 180 / Math.PI) > CurveByInferenceSettings.Instance.MaximumDeltaInDegrees) { //if the resulting curve ('in feature' length with 'found feature' radius) would have a central angle more than MaximumDeltaInDegrees degrees then skip Marshal.ReleaseComObject(foundFeature); continue; } /****************************** * * Compare two lines, one from each in geometry and found geometry taken at the closes ends of the lines * Ie, compare the slopes of the two lines where they touch (or at least where they closest). * * ****************************/ //if geometry of curve neighbor curves have been cracked then there can be more than one segment //however since all segments would be circular arcs, just need to test the first segment ISegmentCollection pFoundLineGeomSegs = foundLineGeom as ISegmentCollection; ISegment pSeg = pFoundLineGeomSegs.get_Segment(0); if (!(pSeg is ICircularArc)) { Marshal.ReleaseComObject(foundFeature); continue; } IVector3D vect1 = (IVector3D)new Vector3D(); IVector3D vect2 = (IVector3D)new Vector3D(); ILine tang = new Line(); double dUnitSignChange = 1; if (iRelativeOrientation == RelativeOrientation.To_To) //closest points are original TO and found TO { dUnitSignChange = -1; vect1.PolarSet(inGeomTangentLineAtEnd.Angle, 0, 1); foundPolyCurve.QueryTangent(esriSegmentExtension.esriExtendAtTo, 1.0, true, 1, tang); vect2.PolarSet(tang.Angle, 0, 1); } else if (iRelativeOrientation == RelativeOrientation.To_From)//closest points are original TO and found FROM { vect1.PolarSet(inGeomTangentLineAtEnd.Angle, 0, 1); foundPolyCurve.QueryTangent(esriSegmentExtension.esriExtendAtFrom, 0.0, true, 1, tang); vect2.PolarSet(tang.Angle, 0, 1); } else if (iRelativeOrientation == RelativeOrientation.From_To)//closest points are original FROM and found TO { vect1.PolarSet(inGeomTangentLineAtStart.Angle, 0, 1); foundPolyCurve.QueryTangent(esriSegmentExtension.esriExtendAtTo, 1.0, true, 1, tang); vect2.PolarSet(tang.Angle, 0, 1); } else if (iRelativeOrientation == RelativeOrientation.From_From)//closest points are original FROM and found FROM { dUnitSignChange = -1; vect1.PolarSet(inGeomTangentLineAtStart.Angle, 0, 1); foundPolyCurve.QueryTangent(esriSegmentExtension.esriExtendAtFrom, 0.0, true, 1, tang); vect2.PolarSet(tang.Angle, 0, 1); } double tangentAngleDelta = Math.Abs(Math.Acos(vect1.DotProduct(vect2)) * 180 / Math.PI); //in degrees if (tangentAngleDelta < CurveByInferenceSettings.Instance.AngleToleranceTangentCompareInDegrees || (180 - tangentAngleDelta) < CurveByInferenceSettings.Instance.AngleToleranceTangentCompareInDegrees) { double inferredRadius = foundRadius * dUnitSignChange; CurveInfoFromNeighbours.Add(new RelatedCurve(foundFeature.OID, inferredRadius, foundCentriodID.Value, iRelativeOrientation)); } Marshal.ReleaseComObject(foundFeature); } Marshal.FinalReleaseComObject(pFeatCursLines); if (bHasTangentStraightLineAtJunction) CurveInfoFromNeighbours.Clear(); //open to logic change to be less conservative //Original source didn't clear the list before, returing all the found records by reference. //calling procedure didn't do anything with those features though, only consumed the features if true was returned return CurveInfoFromNeighbours; }
public bool HasTangentCurveMatchFeatures(IFeatureClass FeatureClass, IPolycurve inPolycurve, string WhereClause, double AngleToleranceTangentCompareInDegrees, double StraightLinesBreakLessThanInDegrees, double MaximumDeltaInDegrees, double ExcludeTangentsShorterThan, out int outFoundTangentCurvesCount, ref List<string> CurveInfoFromNeighbours) { outFoundTangentCurvesCount = 0; ILine pOriginalChord = new Line(); pOriginalChord.PutCoords(inPolycurve.FromPoint, inPolycurve.ToPoint); IVector3D vecOriginalSelected = new Vector3DClass(); vecOriginalSelected.PolarSet(pOriginalChord.Angle, 0, 1); int idxRadius = FeatureClass.FindField("RADIUS"); if (idxRadius == -1) return false; int idxCenterPointID = FeatureClass.FindField("CENTERPOINTID"); if (idxCenterPointID == -1) return false; object val = null; IGeometryBag pGeomBag = new GeometryBagClass(); IGeometryCollection pGeomColl = (IGeometryCollection)pGeomBag; IGeometry MultiPartPolyLine = new PolylineClass(); //qi IGeoDataset pGeoDS = (IGeoDataset)FeatureClass; ISpatialReference spatialRef = pGeoDS.SpatialReference; MultiPartPolyLine.SpatialReference = spatialRef; IGeometryCollection geometryCollection2 = MultiPartPolyLine as IGeometryCollection; ILine pTangentLineAtEnd = new Line(); //new ILine pTangentLineAtStart = new Line(); //new object objMissing = Type.Missing; for (int i = 0; i < 2; i++) { ILine pThisLine = null; if (i == 0) { inPolycurve.QueryTangent(esriSegmentExtension.esriExtendAtTo, 1.0, true, 0.2, pTangentLineAtEnd); pThisLine = new Line(); pThisLine.PutCoords(pTangentLineAtEnd.FromPoint, pTangentLineAtEnd.ToPoint); pGeomColl.AddGeometry(pThisLine); } else { inPolycurve.QueryTangent(esriSegmentExtension.esriExtendAtFrom, 0.0, true, 0.2, pTangentLineAtStart); pThisLine = new Line(); pThisLine.PutCoords(pTangentLineAtStart.FromPoint, pTangentLineAtStart.ToPoint); pGeomColl.AddGeometry(pThisLine); } //Create a new path for each line. ISegmentCollection newPath = new PathClass(); newPath.AddSegment((ISegment)pThisLine, ref objMissing, ref objMissing); //The spatial reference associated with geometryCollection will be assigned to all incoming paths and segments. geometryCollection2.AddGeometry(newPath as IGeometry, ref objMissing, ref objMissing); } //now buffer the lines IGeometryCollection outBufferedGeometryCol = new GeometryBagClass(); for (int jj = 0; jj < geometryCollection2.GeometryCount; jj++) { IPath pPath = geometryCollection2.get_Geometry(jj) as IPath; IGeometryCollection pPolyL = new PolylineClass(); pPolyL.AddGeometry((IGeometry)pPath); ITopologicalOperator topologicalOperator = (ITopologicalOperator)pPolyL; IPolygon pBuffer = topologicalOperator.Buffer(0.1) as IPolygon; outBufferedGeometryCol.AddGeometry(pBuffer, ref objMissing, ref objMissing); } ITopologicalOperator pUnionedBuffers = null; pUnionedBuffers = new PolygonClass() as ITopologicalOperator; pUnionedBuffers.ConstructUnion((IEnumGeometry)outBufferedGeometryCol); ISpatialFilter pSpatFilt = new SpatialFilter(); pSpatFilt.WhereClause = WhereClause; pSpatFilt.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects; pSpatFilt.SearchOrder = esriSearchOrder.esriSearchOrderSpatial; pSpatFilt.Geometry = (IGeometry)pUnionedBuffers; IFeatureCursor pFeatCursLines = null; try { pFeatCursLines = FeatureClass.Search(pSpatFilt, false); } catch (Exception ex) { MessageBox.Show(ex.Message); return false; } IVector3D vecFoundGeom = new Vector3DClass(); IFeature pFeat = pFeatCursLines.NextFeature(); bool bHasTangentStraightLineAtJunction = false; List<int> lstLargeBreak = new List<int>(); while (pFeat != null) { IGeometry pFoundLineGeom = pFeat.ShapeCopy; IPolycurve pFoundLineAsPolyCurve = pFoundLineGeom as IPolycurve; int iRelativeOrientation = GetRelativeOrientation(pFoundLineAsPolyCurve, inPolycurve); //iRelativeOrientation == 1 --> closest points are original TO and found TO //iRelativeOrientation == 2 --> closest points are original TO and found FROM //iRelativeOrientation == 3 --> closest points are original FROM and found TO //iRelativeOrientation == 4 --> closest points are original FROM and found FROM //if the feature has no radius attribute, skip. double dRadius = 0; int iCtrPoint = -1; val = pFeat.get_Value(idxRadius); if (val == DBNull.Value) dRadius = 0; else dRadius = (double)val; val = pFeat.get_Value(idxCenterPointID); IPolycurve pPolyCurve = pFoundLineGeom as IPolycurve; ILine pFoundChordCandidate = new LineClass(); pFoundChordCandidate.PutCoords(pPolyCurve.FromPoint, pPolyCurve.ToPoint); //first check for liklihood that subject line is supposed to stay straight, by //geometry chord bearing angle break test vecFoundGeom.PolarSet(pFoundChordCandidate.Angle, 0, 1); double dDotProd = vecFoundGeom.DotProduct(vecOriginalSelected); double dAngleCheck = Math.Acos(dDotProd) * 180 / Math.PI; //in degrees dAngleCheck = Math.Abs(dAngleCheck); double dLargeAngleBreakInDegrees = 3; if (dAngleCheck > dLargeAngleBreakInDegrees && dAngleCheck < (180 - dLargeAngleBreakInDegrees)) //large angle break non-tangent, greater than 3 degrees { if (!lstLargeBreak.Contains(iRelativeOrientation)) lstLargeBreak.Add(iRelativeOrientation); } if ((dAngleCheck <= StraightLinesBreakLessThanInDegrees || (180 - dAngleCheck) < StraightLinesBreakLessThanInDegrees) && val == DBNull.Value && dRadius == 0 && !(pPolyCurve.Length< ExcludeTangentsShorterThan)) { if (lstLargeBreak.Contains(iRelativeOrientation)) bHasTangentStraightLineAtJunction = true; } if (val == DBNull.Value || dRadius == 0 || pPolyCurve.Length< ExcludeTangentsShorterThan) {//if the feature has a null centrpointID then skip. Marshal.ReleaseComObject(pFeat); pFeat = pFeatCursLines.NextFeature(); continue; } if (Math.Abs(inPolycurve.Length / dRadius * 180 / Math.PI) > MaximumDeltaInDegrees) { //if the resulting curve would have a central angle more than MaximumDeltaInDegrees degrees then skip Marshal.ReleaseComObject(pFeat); pFeat = pFeatCursLines.NextFeature(); continue; } iCtrPoint = (int)val; //if geometry of curve neighbour curves have been cracked then there can be more than one segment //however since all segments would be circular arcs, just need to test the first segment ISegmentCollection pFoundLineGeomSegs = pFoundLineGeom as ISegmentCollection; ISegment pSeg = pFoundLineGeomSegs.get_Segment(0); if (!(pSeg is ICircularArc)) { Marshal.ReleaseComObject(pFeat); pFeat = pFeatCursLines.NextFeature(); continue; } dRadius = (double)pFeat.get_Value(idxRadius); IVector3D vect1 = new Vector3DClass(); IVector3D vect2 = new Vector3DClass(); ILine tang = new Line(); double dUnitSignChange = 1; if (iRelativeOrientation == 1) //closest points are original TO and found TO { dUnitSignChange = -1; vect1.PolarSet(pTangentLineAtEnd.Angle, 0, 1); pFoundLineAsPolyCurve.QueryTangent(esriSegmentExtension.esriExtendAtTo, 1.0, true, 1, tang); vect2.PolarSet(tang.Angle, 0, 1); } else if (iRelativeOrientation == 2)//closest points are original TO and found FROM { vect1.PolarSet(pTangentLineAtEnd.Angle, 0, 1); pFoundLineAsPolyCurve.QueryTangent(esriSegmentExtension.esriExtendAtFrom, 0.0, true, 1, tang); vect2.PolarSet(tang.Angle, 0, 1); } else if (iRelativeOrientation == 3)//closest points are original FROM and found TO { vect1.PolarSet(pTangentLineAtStart.Angle, 0, 1); pFoundLineAsPolyCurve.QueryTangent(esriSegmentExtension.esriExtendAtTo, 1.0, true, 1, tang); vect2.PolarSet(tang.Angle, 0, 1); } else if (iRelativeOrientation == 4)//closest points are original FROM and found FROM { dUnitSignChange = -1; vect1.PolarSet(pTangentLineAtStart.Angle, 0, 1); pFoundLineAsPolyCurve.QueryTangent(esriSegmentExtension.esriExtendAtFrom, 0.0, true, 1, tang); vect2.PolarSet(tang.Angle, 0, 1); } dDotProd = vect1.DotProduct(vect2); dAngleCheck = Math.Acos(dDotProd) * 180 / Math.PI; //in degrees dAngleCheck = Math.Abs(dAngleCheck); if (dAngleCheck < AngleToleranceTangentCompareInDegrees || (180 - dAngleCheck) < AngleToleranceTangentCompareInDegrees) { double dDerivedRadius = dRadius * dUnitSignChange; string sHarvestedCurveInfo = pFeat.OID.ToString() + "," + dDerivedRadius.ToString("#.000") + "," + iCtrPoint.ToString() + "," + "t"; CurveInfoFromNeighbours.Add(sHarvestedCurveInfo); outFoundTangentCurvesCount++; } Marshal.ReleaseComObject(pFeat); pFeat = pFeatCursLines.NextFeature(); } Marshal.FinalReleaseComObject(pFeatCursLines); if (bHasTangentStraightLineAtJunction) return false; //open to logic change to be less conservative bool bHasParallelCurveFeaturesNearby = (outFoundTangentCurvesCount > 0); return bHasParallelCurveFeaturesNearby; }