List<RelatedCurve> GetParallelCurveMatchFeatures(IFeatureClass FeatureClass, IFeature inFeature, IPolycurve inPolycurve, string WhereClause) //double AngleToleranceTangentCompareInDegrees, double OrthogonalSearchDistance, // out int outFoundLinesCount, out int outFoundParallelCurvesCount, ref List<RelatedCurve> CurveInfoFromNeighbours) { List<RelatedCurve> CurveInfoFromNeighbours = new List<RelatedCurve>(); ILine inGeomChord = (ILine)new Line(); inGeomChord.PutCoords(inPolycurve.FromPoint, inPolycurve.ToPoint); IVector3D inGeomVector = (IVector3D)new Vector3D(); inGeomVector.PolarSet(inGeomChord.Angle, 0, 1); //generate line segments that are perpendicular to the in feature at half way IGeometryBag queryGeomBag = (IGeometryBag)new GeometryBag(); IGeometryCollection queryGeomPartCollection = (IGeometryCollection)queryGeomBag; IGeometry queryMultiPartPolyLine = (IGeometry)new Polyline(); //qi IGeoDataset pGeoDS = (IGeoDataset)FeatureClass; ISpatialReference spatialRef = pGeoDS.SpatialReference; queryMultiPartPolyLine.SpatialReference = spatialRef; IGeometryCollection queryGeomCollection = queryMultiPartPolyLine as IGeometryCollection; ILine pNormalLine = (ILine)new Line(); //new for (int i = -1; i < 2; i = i + 2) { double dOffset = CurveByInferenceSettings.Instance.OrthogonalSearchDistance * i; inPolycurve.QueryNormal(esriSegmentExtension.esriNoExtension, 0.5, true, dOffset, pNormalLine); ILine pThisLine = (ILine)new Line(); pThisLine.PutCoords(pNormalLine.FromPoint, pNormalLine.ToPoint); queryGeomPartCollection.AddGeometry(pThisLine); //Although each line is connected to the other, create a new path for each line //this allows for future changes in case the input needs to be altered to separate paths. ISegmentCollection newPath = (ISegmentCollection)new Path(); object obj = Type.Missing; newPath.AddSegment((ISegment)pThisLine, ref obj, ref obj); //The spatial reference associated with geometryCollection will be assigned to all incoming paths and segments. queryGeomCollection.AddGeometry(newPath as IGeometry, ref obj, ref obj); } //search for records that intersect these perpendicular geometries IQueryFilter filter = new SpatialFilter(); filter.SubFields = String.Format("{0}, {1}, {2}, {3}", FeatureClass.OIDFieldName, CurveByInferenceSettings.Instance.RadiusFieldName, CurveByInferenceSettings.Instance.CenterpointIDFieldName, FeatureClass.ShapeFieldName); ISpatialFilter spatialFilter = (ISpatialFilter)filter; //Can't add any additional filtering on centerpointid and radius field because the straight lines are needed spatialFilter.WhereClause = WhereClause; spatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects; spatialFilter.SearchOrder = esriSearchOrder.esriSearchOrderSpatial; spatialFilter.Geometry = queryGeomBag; IFeatureCursor pFeatCursLines = null; try { pFeatCursLines = FeatureClass.Search(spatialFilter, false); } catch (Exception ex) { messageBox.Show(ex.Message); return CurveInfoFromNeighbours; } int idxCenterPointID = pFeatCursLines.Fields.FindField(CurveByInferenceSettings.Instance.CenterpointIDFieldName); int idxRadius = pFeatCursLines.Fields.FindField(CurveByInferenceSettings.Instance.RadiusFieldName); IPoint midpoint = new Point(); inPolycurve.QueryPoint(esriSegmentExtension.esriNoExtension, 0.5, true, midpoint); double lengthFiler = inPolycurve.Length * 3; double closestStraighLine = Double.MaxValue; IFeature pFeat = null; while ((pFeat = pFeatCursLines.NextFeature()) != null) { if (inFeature.OID == pFeat.OID) continue; IGeometry pFoundLineGeom = pFeat.ShapeCopy; IPolyline pFoundPolyline = pFoundLineGeom as IPolyline; ITopologicalOperator6 pTopoOp6 = (ITopologicalOperator6)queryMultiPartPolyLine; IGeometry intersectionPoint = pTopoOp6.IntersectEx(pFoundLineGeom, false, esriGeometryDimension.esriGeometry0Dimension); if (intersectionPoint == null || intersectionPoint.IsEmpty) { // there isn't actually an intersection between the features Marshal.ReleaseComObject(pFeat); continue; } //distance from intersection of tangent line and found feature to the start point of the tangent line double distanceToLine = ((IProximityOperator)intersectionPoint).ReturnDistance(midpoint); //if the feature has no radius attribute, skip. double dRadius = pFeat.get_Value(idxRadius) is DBNull ? 0 : (double)pFeat.get_Value(idxRadius); int? centerpointID = pFeat.get_Value(idxCenterPointID) is DBNull ? null : (int?)pFeat.get_Value(idxCenterPointID); if (dRadius == 0 || centerpointID == null) {//null centrpointID so skip. if (closestStraighLine > distanceToLine && pFoundPolyline.Length > lengthFiler) { closestStraighLine = distanceToLine; CurveInfoFromNeighbours.RemoveAll(w=>w.DistanceToLine > closestStraighLine); } Marshal.ReleaseComObject(pFeat); continue; } //out past a straight line if (closestStraighLine < distanceToLine) continue; ISegmentCollection pFoundLineGeomSegs = pFoundLineGeom as ISegmentCollection; bool bHasCurves = false; pFoundLineGeomSegs.HasNonLinearSegments(ref bHasCurves); if (!bHasCurves) { Marshal.ReleaseComObject(pFeat); continue; } IPointCollection5 PtColl = (IPointCollection5)intersectionPoint; if (PtColl.PointCount > 1) { // the intersection isn't a point Marshal.ReleaseComObject(pFeat); continue; } IPolycurve pPolyCurve4Tangent = pFoundLineGeom as IPolycurve; for (int j = 0; j < PtColl.PointCount; j++) { IPoint p = PtColl.get_Point(j); IPoint outPoint = (IPoint)new Point(); double dDistanceAlong = 0; double dDistanceFromCurve = 0; bool bOffsetRight = true; //work out if the point is to the left or right of the original inPolycurve.QueryPointAndDistance(esriSegmentExtension.esriNoExtension, p, false, outPoint, ref dDistanceAlong, ref dDistanceFromCurve, ref bOffsetRight); ILine pTangent = (ILine)new Line(); dDistanceAlong = 0; dDistanceFromCurve = 0; bool bOnRight = true; pPolyCurve4Tangent.QueryPointAndDistance(esriSegmentExtension.esriNoExtension, p, false, outPoint, ref dDistanceAlong, ref dDistanceFromCurve, ref bOnRight); pPolyCurve4Tangent.QueryTangent(esriSegmentExtension.esriNoExtension, dDistanceAlong, false, 100, pTangent); //compare the tangent bearing with the normal to check for orthogonality IVector3D vecTangent = (IVector3D)new Vector3D(); vecTangent.PolarSet(pTangent.Angle, 0, 1); IVector3D vecNormal = (IVector3D)new Vector3D(); vecNormal.PolarSet(pNormalLine.Angle, 0, 1); ILine pHitDistanceForRadiusDifference = (ILine)new Line(); pHitDistanceForRadiusDifference.PutCoords(pNormalLine.FromPoint, outPoint); double dRadiusDiff = pHitDistanceForRadiusDifference.Length; double dDotProd = vecTangent.DotProduct(vecNormal); double dAngleCheck = Math.Acos(dDotProd) * 180 / Math.PI; //in degrees dAngleCheck = Math.Abs(dAngleCheck - 90); if (dAngleCheck < CurveByInferenceSettings.Instance.AngleToleranceTangentCompareInDegrees) { //work out concavity orientation with respect to the original line using the radius sign and dot product dDotProd = inGeomVector.DotProduct(vecTangent); double dTangentCheck = Math.Acos(dDotProd) * 180 / Math.PI; // in degrees //dTangentCheck at this point should be close to 0 or 180 degrees. bool bIsConvex = ((dTangentCheck < 90 && dRadius < 0 && !bOffsetRight) || (dTangentCheck > 90 && dRadius > 0 && !bOffsetRight) || (dTangentCheck < 90 && dRadius > 0 && bOffsetRight) || (dTangentCheck > 90 && dRadius < 0 && bOffsetRight)); double dUnitSignChange = 1; if (!bIsConvex) dUnitSignChange = -1; double dDerivedRadius = (Math.Abs(dRadius)) + dRadiusDiff * dUnitSignChange; dUnitSignChange = 1; //now compute inferred left/right for candidate if (bIsConvex && !bOffsetRight) dUnitSignChange = -1; if (!bIsConvex && bOffsetRight) dUnitSignChange = -1; dDerivedRadius = dDerivedRadius * dUnitSignChange; //string sHarvestedCurveInfo = pFeat.OID.ToString() + "," + dDerivedRadius.ToString("#.000") + "," + centerpointID.ToString() + "," + dRadiusDiff.ToString("#.000"); CurveInfoFromNeighbours.Add(new RelatedCurve(pFeat.OID, dDerivedRadius, centerpointID.Value, distanceToLine, RelativeOrientation.Parallel)); } } Marshal.ReleaseComObject(pFeat); } Marshal.FinalReleaseComObject(pFeatCursLines); return CurveInfoFromNeighbours; }