bool evaluateTangent(InferredCurve inferredCurve, RelatedCurve curve)
        {
            //double angle = Math.Atan((inferredCurve.FromPoint.Y - inferredCurve.ToPoint.Y) / (inferredCurve.FromPoint.X - inferredCurve.ToPoint.X));
            //double length = ((IProximityOperator)inferredCurve.FromPoint).ReturnDistance(inferredCurve.ToPoint);
            
            //the proposed arc chord:
            ILine line = new Line() { FromPoint = inferredCurve.FromPoint, ToPoint = inferredCurve.ToPoint };

            //half the delta of the proposed curve would be:
            double halfdelta = toDegrees(Math.Asin(line.Length / 2 / curve.Radius));

            IVector3D chordVector = new Vector3D() as IVector3D;
            chordVector.PolarSet(line.Angle, 0, 1);

            foreach (RelatedLine tangent in inferredCurve.TangentLines)
            {
                IVector3D tangentVector = new Vector3D() as IVector3D;
                tangentVector.PolarSet(ToRadians(tangent.Angle), 0, 1);

                bool dMatch = false;
                double dVectDiff = toDegrees(Math.Acos(chordVector.DotProduct(tangentVector)));
                if (tangent.Orientation == RelativeOrientation.From_To)
                    dMatch = Math.Abs(dVectDiff - halfdelta) < CurveByInferenceSettings.Instance.MaxTangentLineAngleInDegrees;
                else if (tangent.Orientation == RelativeOrientation.To_From)
                    dMatch = Math.Abs(dVectDiff + halfdelta) < CurveByInferenceSettings.Instance.MaxTangentLineAngleInDegrees;
                else if (tangent.Orientation == RelativeOrientation.From_From)
                    dMatch = Math.Abs(dVectDiff + halfdelta) < CurveByInferenceSettings.Instance.MaxTangentLineAngleInDegrees;
                else if (tangent.Orientation == RelativeOrientation.To_To)
                    dMatch = Math.Abs(dVectDiff - halfdelta) < CurveByInferenceSettings.Instance.MaxTangentLineAngleInDegrees;
                if (dMatch)
                {
                    inferredCurve.InferredRadius = inferredCurve.TangentCurves[0].Radius;
                    inferredCurve.InferredCenterpointID = inferredCurve.TangentCurves[0].CenterpointID;
                    return true;
                }
            }
            return false;
        }
        private static object GenerateConstructorStatment_Curve(InferredCurve curve)
        {
            StringBuilder strBuilder = new StringBuilder();
            strBuilder.Append(String.Format(CreateCurve, curve.ObjectID));
            strBuilder.Append(String.Join(ListJoin, (from r in curve.TangentCurves select String.Format(CreateRelatedCurve, r.ObjectID, r.Radius, r.CenterpointID, r.Orientation)).ToArray()));
            strBuilder.Append(CreateCurveClose);

            if(curve.HasValue)
                strBuilder.AppendFormat(AcceptedCreate, curve.InferredRadius, curve.InferredCenterpointID);

            strBuilder.Append(ParallelCreate);
            strBuilder.Append(String.Join(ListJoin, (from r in curve.ParallelCurves select String.Format(CreateCurveItem, r.ObjectID, r.Radius, r.CenterpointID, r.Orientation)).ToArray()));
            strBuilder.Append(ParallelCreateClose);
            strBuilder.Append(LineCreate);
            strBuilder.Append(String.Join(ListJoin, (from r in curve.TangentLines select String.Format(CreateLineItem, r.ObjectID, r.Angle, r.DeltaAngle, r.Orientation)).ToArray()));
            strBuilder.Append(Environment.NewLine + LineCreateClose);

            return strBuilder.ToString();
        }
 bool evaluateParallelCurves(InferredCurve inferredCurve, RelatedCurve curve)
 {
     bool bHasConfirmer = false;
     foreach (RelatedCurve dd in inferredCurve.ParallelCurves)
     {
         if (Math.Abs(dd.Radius - curve.Radius) < CurveByInferenceSettings.Instance.MaxRadiusDifference)
         {
             bHasConfirmer = true;
             break;
         }
     }
     if (bHasConfirmer)
     {
         inferredCurve.InferredRadius = inferredCurve.TangentCurves[0].Radius;
         inferredCurve.InferredCenterpointID = inferredCurve.TangentCurves[0].CenterpointID;
         return true;
     }
     return false;
 }
        private bool evaluateJunctions(InferredCurve curve, List<RelatedLine> tangentLines)
        {
            //count the perpendicular lines at the start and end points of the line
            List<RelatedLine> startPerpendicular = tangentLines.Where(t => t.isAtStart && t.DeltaAngle > 90 - CurveByInferenceSettings.Instance.PerpendicularTolerance && t.DeltaAngle < 90 + CurveByInferenceSettings.Instance.PerpendicularTolerance).ToList();
            List<RelatedLine> endPerpendicular = tangentLines.Where(t => t.isAtEnd && t.DeltaAngle > 90 - CurveByInferenceSettings.Instance.PerpendicularTolerance && t.DeltaAngle < 90 + CurveByInferenceSettings.Instance.PerpendicularTolerance).ToList();

            //if there aren't any perpendicular lines
            if (startPerpendicular.Count == 0 && endPerpendicular.Count == 0)
                return true;

            //if both ends have perpendicular lines
            if (startPerpendicular.Count > 0 && endPerpendicular.Count > 0)
                return true;

            List<RelatedLine> perpendiculars = endPerpendicular;
            bool atStart = false;
            if (startPerpendicular.Count > 0 && endPerpendicular.Count == 0)
            {
                atStart = true;
                perpendiculars = startPerpendicular;
            }
            
            //determin what lines are actually present
            List<RelatedCurve> curves = null;
            List<RelatedLine> tangents = null;
            if (atStart)
            {
                curves = curve.TangentCurves.Where(w => w.isAtStart).ToList();
                tangents = tangentLines.Where(w => w.isAtStart && w.DeltaAngle < 5).ToList();
            }
            else
            {
                curves = curve.TangentCurves.Where(w => w.isAtEnd).ToList();
                tangents = tangentLines.Where(w => w.isAtEnd && w.DeltaAngle < 5).ToList();
            }

            //int perpendicularCount = perpendiculars.Select(w => w.DeltaAngle).Distinct().Count();

            var groupsAngle = tangents.GroupBy(item => item, relatedLineComparer);
            //var groupsRadiusAndCP = curves.GroupBy(item => item, relatedCurveComparer).Where(group => group.Skip(1).Any());
            var groupsRadiusAndCP = curves.GroupBy(item => item, relatedCurveComparer);

            if (groupsAngle.Count() == 1 && groupsRadiusAndCP.Count() == 0)
            {
                // only a straight line on the other side of the perpendicular
                return false;
            }
            else if (groupsAngle.Count() == 0 && groupsRadiusAndCP.Count() == 1)
            {
                // only a curved line on the other side of the perpendicular, so set the curve
                curve.InferredCenterpointID = curves[0].CenterpointID;
                curve.InferredRadius = curves[0].Radius;
                curve.TangentLines = tangentLines;
                return true;
            }
            //no comment, let the rest of the logic look through everything
            return true;

        }
        public void FindCurves(String Name, IFeatureClass pFabricLinesFC, ISelectionSet selSet, string whereClause, myProgessor progressor)
        {
            CurveByInferenceSettings.FieldPositions positions = new CurveByInferenceSettings.FieldPositions((ITable)pFabricLinesFC);
            if (!positions.ValidCheckFields)
            {
                messageBox.Show(string.Format("One or more of the following fields are missing ({0}, {1})", CurveByInferenceSettings.Instance.RadiusFieldName, CurveByInferenceSettings.Instance.CenterpointIDFieldName));
            }


            IQueryFilter qFilter = new QueryFilter();
            if (String.IsNullOrEmpty(whereClause))
            {
                qFilter.WhereClause = "CenterPointID is null and Radius is null";
            }
            else
            {
                qFilter.WhereClause = string.Concat(whereClause, " and CenterPointID is null and Radius is null");
            }

            IFeatureCursor cursor = null;
            if (selSet != null)
            {
                ICursor c;
                ISelectionSet subset = selSet.Select(qFilter, esriSelectionType.esriSelectionTypeIDSet, esriSelectionOption.esriSelectionOptionNormal, null);
                progressor.setStepProgressorProperties(subset.Count, String.Format("Layer {0}: Evaluating Selected Features", Name));

                subset.Search(null, true, out c);
                cursor = (IFeatureCursor)c;
            }
            else
            {
                progressor.setStepProgressorProperties(((ITable)pFabricLinesFC).RowCount(qFilter), String.Format("Layer {0}: Evaluating Features", Name));
                cursor = pFabricLinesFC.Search(qFilter, true);
            }

            //ISelectionSet pSelSet = pFabricLinesFC.Select(qFilter, esriSelectionType.esriSelectionTypeIDSet, esriSelectionOption.esriSelectionOptionNormal, null);
            //progressor.setStepProgressorProperties(pSelSet.Count, String.Format("Layer {0}: Evaluating Features", Name));
            //ICursor cursor = null;
            //pSelSet.Search(null, false, out cursor);


            IFeature pLineFeat = null;
            while ((pLineFeat = (IFeature)cursor.NextFeature() as IFeature) != null)
            {
                if (!progressor.Continue())
                    break;
                progressor.Step();

                if (!Curves.Any(w => w.ObjectID == pLineFeat.OID))
                {
                    IGeometry pGeom = pLineFeat.ShapeCopy;
                    ISegmentCollection pSegColl = pGeom as ISegmentCollection;
                    ISegment pSeg = null;
                    if (pSegColl != null && pSegColl.SegmentCount == 1)
                    {
                        pSeg = pSegColl.get_Segment(0);
                    }
                    else
                    {
                        if (pSegColl == null)
                            messageBox.Show(String.Format("The shape for objectid {0} could not be converted to a segement collection.", pLineFeat.OID));

                        //todo: but for now, only deals with single segment short segments
                        Marshal.ReleaseComObject(pLineFeat);
                        continue;
                    }

                    //if the geometry is a circular arc and the attributes reflect that, move on to the next feature
                    //obsolete, filter is pushed to database
                    //if (pSeg is ICircularArc)
                    //{
                    //    object dVal1 = pLineFeat.get_Value(idxRADIUS);
                    //    object dVal2 = pLineFeat.get_Value(idxCENTERPTID);
                    //    if (!(dVal1 is DBNull) && !(dVal2 is DBNull))
                    //    {
                    //        Marshal.ReleaseComObject(pLineFeat);
                    //        continue;
                    //    }
                    //}

                    //query near lines
                    List<RelatedLine> tangentLines;
                    List<RelatedCurve> sCurveInfoFromNeighbours = GetTangentCurveMatchFeatures(pFabricLinesFC, pLineFeat, (IPolycurve)pGeom, "", positions.RadiusFieldIdx, positions.CenterpointIDFieldIdx, pSeg.Length, out tangentLines);
                                
                    if(sCurveInfoFromNeighbours.Count > 0)
                    //if (HasTangentCurveMatchFeatures(pFabricLinesFC, (IPolycurve)pGeom, "", pSeg.Length, out iFoundTangent, ref sCurveInfoFromNeighbours))
                    {
                        InferredCurve curve = new InferredCurve(pLineFeat.OID, Name, sCurveInfoFromNeighbours);
                        IPolyline polyLine = (IPolyline)pGeom;
                        curve.FromPoint = polyLine.FromPoint;
                        curve.ToPoint = polyLine.ToPoint;
                     
                        //evaluated the position of any tangent lines, and determines if there is a junction situation.  If there is, it will:
                        //  Retrun true with curve.Accepted set
                        //  Return true without curve.Accepted set
                        //  Return false (this indicates that the junction has been used to verify that this segemnt should not have a curve)
                        if (evaluateJunctions(curve, tangentLines))
                        {
                            //junction couldn't eliminate the curve, so return it
                            Curves.Add(curve);
                            curve.PropertyChanged += new PropertyChangedEventHandler(curve_PropertyChanged);
                            curve.Parcel = (int)pLineFeat.get_Value(positions.ParcelIDFieldIdx);

                            if (!curve.HasValue) //if the junction logic didn't set the curve
                            {
                                //check to see if one of the tange curves overlap
                                if (curve.TangentCurves[0].Orientation == RelativeOrientation.Same || curve.TangentCurves[0].Orientation == RelativeOrientation.Reverse)
                                {
                                    curve.InferredRadius = curve.TangentCurves[0].Radius;
                                    curve.InferredCenterpointID = curve.TangentCurves[0].CenterpointID;
                                }
                                else
                                {
                                    //check radial and tangent lines (both need a single centerpoint and radius tangent curve (check done in function)
                                    RefineToBestRadiusAndCenterPoint(curve, pFabricLinesFC, pLineFeat, (IPolycurve)pGeom, tangentLines);
                                }
                            }

                            //if the curve has an accepted curve (ie, it's a candidate to be changed), record the parcel id
                            //if (curve.Accepted != null)
                            //    affectedParcels.Add();

                            //cache the parcel so it can be looked up later
                        }
                    }
                }
                Marshal.ReleaseComObject(pLineFeat);
            }
            Marshal.ReleaseComObject(cursor);

            Total = Curves.Count;
            Inferred = Curves.Count(w => w.Action == UpdateAction.Update);
        }
        void RefineToBestRadiusAndCenterPoint(InferredCurve inferredCurve, IFeatureClass pFabricLinesFC, IFeature pLineFeat, IPolycurve polyCurve, List<RelatedLine> tangentLines)
        {
            //only one radius found, simple case
            if (inferredCurve.TangentCurves.Count == 1)
            {
                //search the parallel offsets for one conformer, if found return
                inferredCurve.ParallelCurves = GetParallelCurveMatchFeatures(pFabricLinesFC, pLineFeat, polyCurve, "");
                if (inferredCurve.ParallelCurves.Count > 0 && evaluateParallelCurves(inferredCurve, inferredCurve.TangentCurves[0]))
                {
                    return;
                }

                //search the tagent lines for one conformer, if found return
                inferredCurve.TangentLines = tangentLines;
                if (inferredCurve.TangentLines.Count > 0 && evaluateTangent(inferredCurve, inferredCurve.TangentCurves[0]))
                {
                    return;
                }
            }
            else  //if there is more than one tangent curve, try to group them together
            {
                var groupsTangent = inferredCurve.TangentCurves.GroupBy(item => Math.Round(item.Radius, 2)).Where(group => group.Skip(1).Any());
                var groupsTangentAndCP = inferredCurve.TangentCurves.GroupBy(item => item, relatedCurveComparer).Where(group => group.Skip(1).Any());

                //System.Diagnostics.Debug.Print(groupsTangent.Count().ToString());
                //System.Diagnostics.Debug.Print(groupsTangentAndCP.Count().ToString());

                bool HasStartTangents = inferredCurve.TangentCurves.Any(w => w.Orientation == RelativeOrientation.To_From || w.Orientation == RelativeOrientation.To_To);
                bool HasEndTangents = inferredCurve.TangentCurves.Any(w => w.Orientation == RelativeOrientation.From_To || w.Orientation == RelativeOrientation.From_From);

                //if there is only 1 of each group, then there are no ambiguities for the tangent or the center point
                if (groupsTangent.Count() == 1 && groupsTangentAndCP.Count() == 1)
                {
                    IGrouping<RelatedCurve, RelatedCurve> d1 = groupsTangentAndCP.ElementAt(0);

                    //if there are curves on either side of the query feature
                    if (HasStartTangents && HasEndTangents)
                    {

                        inferredCurve.InferredRadius = d1.Key.Radius;
                        inferredCurve.InferredCenterpointID = d1.Key.CenterpointID;
                        return;
                    }
                    if (inferredCurve.TangentCurves.Count > 0)
                    {
                        //search the parallel offsets for one conformer, if found return
                        inferredCurve.ParallelCurves = GetParallelCurveMatchFeatures(pFabricLinesFC, pLineFeat, polyCurve, "");
                        if (inferredCurve.ParallelCurves.Count > 0 && evaluateParallelCurves(inferredCurve, d1.Key))
                        {
                            return;
                        }
                        //search the tagent lines for one conformer, if found return
                        inferredCurve.TangentLines = tangentLines;
                        if (inferredCurve.TangentLines.Count > 0 && evaluateTangent(inferredCurve, d1.Key))
                        {
                            return;
                        }
                    }
                }
                else if (groupsTangent.Count() == 1 && groupsTangentAndCP.Count() > 1)
                { //if there is only 1 tangent, but more than one center point then there are center points to merge

                    //TODO: Add center point Merging here
                }
                if (groupsTangent.Count() > 1)
                { //if there is more than 1 tangent, then ...code stub if needed
                    foreach (var value in groupsTangentAndCP)
                    {
                        System.Diagnostics.Debug.Print(value.Key.ToString());
                    }
                }
            }
        }
        private void SelectAndZoom(InferredCurve curve, bool zoom, bool select, bool related)
        {
            IFeatureLayer fl = getLayerByLayerName(curve.LayerName);
            if (fl == null)
                MessageBox.Show(String.Format("The layer {0} could not be found", curve.LayerName));

            List<int> oids = new List<int>();
            oids.Add(curve.ObjectID);
            if (related)
            {
                oids.AddRange(curve.ParallelCurves.Select(w => w.ObjectID));
                oids.AddRange(curve.TangentCurves.Select(w => w.ObjectID));
                oids.AddRange(curve.TangentLines.Select(w => w.ObjectID));
            }

            IEnvelope extent = null;

            if (select)
                ArcMap.Document.FocusMap.ClearSelection();

            IFeatureCursor featureCursor = fl.FeatureClass.GetFeatures(oids.ToArray(), true);
            IFeature feature = null;
            while ((feature = featureCursor.NextFeature()) != null)
            {
                if (extent == null)
                    extent = feature.Shape.Envelope;
                else
                    extent.Union(feature.Shape.Envelope);

                if (select)
                    ArcMap.Document.FocusMap.SelectFeature(fl, feature);

                Marshal.ReleaseComObject(feature);
            }

            if (zoom)
            {
                ZoomTo(extent);
            }
            else
            {
                ArcMap.Document.ActivatedView.Refresh();
            }
        }