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;
        }