Beispiel #1
0
        public static IPolycurve DensifyPolycurve(ref IPolycurve ipolycurve_0, ref int int_0)
        {
            IPolycurve polycurve = Utils3D.CreateClone(ipolycurve_0 as IClone) as IPolycurve;

            polycurve.Densify(Convert.ToDouble(polycurve.Length / (double)int_0), 0.0);
            return(polycurve);
        }
Beispiel #2
0
        private static void VerifyErrorHasZ(IFeatureWorkspace ws)
        {
            IFieldsEdit fields = new FieldsClass();

            fields.AddField(FieldUtils.CreateOIDField());
            fields.AddField(FieldUtils.CreateShapeField(
                                "Shape", esriGeometryType.esriGeometryPolyline,
                                SpatialReferenceUtils.CreateSpatialReference
                                    ((int)esriSRProjCS2Type.esriSRProjCS_CH1903Plus_LV95,
                                    true), 1000, true, false));

            IFeatureClass featureClass =
                DatasetUtils.CreateSimpleFeatureClass(ws, "VerifyErrorHasZ", fields,
                                                      null);

            // make sure the table is known by the workspace
            ((IWorkspaceEdit)ws).StartEditing(false);
            ((IWorkspaceEdit)ws).StopEditing(true);

            IPolycurve line1 = CurveConstruction.StartLine(0, 0, 5)
                               .LineTo(2, 0, 5)
                               .Curve;
            IFeature row1 = featureClass.CreateFeature();

            row1.Shape = line1;
            row1.Store();

            IPolycurve line2 = CurveConstruction.StartLine(-1, 0.02, 5)
                               .LineTo(1, 0.02, 5)
                               .Curve;
            IFeature row2 = featureClass.CreateFeature();

            row2.Shape = line2;
            row2.Store();

            var test = new QaNotNear(featureClass, 0.1, 0.5);

            var runners =
                new List <QaTestRunnerBase>
            {
                new QaTestRunner(test)
                {
                    KeepGeometry = true
                },
                new QaContainerTestRunner(1000, test)
                {
                    KeepGeometry = true
                }
            };

            foreach (QaTestRunnerBase runner in runners)
            {
                runner.Execute();
                Assert.True(runner.ErrorGeometries.Count > 0);
                foreach (IGeometry errorGeometry in runner.ErrorGeometries)
                {
                    Assert.AreEqual(5, errorGeometry.Envelope.ZMin);
                }
            }
        }
Beispiel #3
0
        public void CanTestPolylineFeaturePath()
        {
            IFeatureClass fc = new FeatureClassMock(1, "LineFc",
                                                    esriGeometryType.esriGeometryPolyline);

            IFeature feature = fc.CreateFeature();

            IPolycurve correctPath = CurveConstruction.StartLine(100, 100)
                                     .LineTo(110, 110)
                                     .Curve;
            // note: union converts linear circular arcs to lines -> make sure the arc is not linear
            IPolycurve incorrectPath = CurveConstruction.StartLine(0, 0)
                                       .LineTo(10, 10)
                                       .CircleTo(30, 10)
                                       .Curve;

            feature.Shape = GeometryUtils.Union(correctPath, incorrectPath);
            feature.Store();

            var test   = new QaGeometryConstraint(fc, "$CircularArcCount = 0", perPart: true);
            var runner = new QaTestRunner(test);

            runner.Execute(feature);

            QaError error;

            AssertUtils.OneError(runner,
                                 "GeometryConstraint.ConstraintNotFulfilled.ForShapePart",
                                 out error);

            Assert.True(GeometryUtils.AreEqual(incorrectPath,
                                               GeometryFactory.CreatePolyline(error.Geometry)));
        }
Beispiel #4
0
        /// <summary>
        /// Calculates the result geometries and adds them to the relevant dictionaries.
        /// </summary>
        /// <param name="fromFeatures"></param>
        /// <param name="overlap"></param>
        /// <param name="targetFeaturesForVertexInsertion"></param>
        /// <param name="trackCancel"></param>
        public void CalculateResults(
            [NotNull] IEnumerable <IFeature> fromFeatures,
            [NotNull] IPolycurve overlap,
            [CanBeNull] IEnumerable <IFeature> targetFeaturesForVertexInsertion = null,
            [CanBeNull] ITrackCancel trackCancel = null)
        {
            Assert.ArgumentNotNull(fromFeatures, nameof(fromFeatures));
            Assert.ArgumentNotNull(overlap, nameof(overlap));

            GeometryUtils.AllowIndexing(overlap);

            foreach (IFeature feature in fromFeatures)
            {
                if (trackCancel != null && !trackCancel.Continue())
                {
                    return;
                }

                ProcessFeature(feature, overlap);
            }

            if (targetFeaturesForVertexInsertion != null)
            {
                InsertIntersectingVerticesInTargets(targetFeaturesForVertexInsertion,
                                                    overlap);
            }
        }
        private static int DeleteShortSegments(
            [NotNull] IPolycurve fromPolycurve,
            [NotNull] FeatureVertexInfo vertexInfo,
            bool use2DLengthOnly,
            [CanBeNull] IGeometry inPerimeter)
        {
            IList <esriSegmentInfo> shortSegmentInfos = vertexInfo.ShortSegments;

            Assert.NotNull(shortSegmentInfos);

            _msg.DebugFormat("Deleting {0} short segments from {1}",
                             shortSegmentInfos.Count,
                             GdbObjectUtils.ToString(vertexInfo.Feature));

            Assert.NotNull(vertexInfo.MinimumSegmentLength, "Minimum segment length not set.");
            //double minimumSegmentLength = (double)vertexInfo.MinimumSegmentLength;

            var fromSegmentCollection = (ISegmentCollection)fromPolycurve;

            int originalSegmentCount = fromSegmentCollection.SegmentCount;

            RemoveShortSegments(fromPolycurve, vertexInfo, use2DLengthOnly,
                                inPerimeter);

            fromSegmentCollection.SegmentsChanged();

            //// re-consider cost vs. information need:
            //int remainingCount = GetShortSegments(
            //    fromPolycurve, inPerimeter, minimumSegmentLength, use2DLengthOnly).Count;

            int deletedSegmentsCount = originalSegmentCount -
                                       fromSegmentCollection.SegmentCount;

            return(deletedSegmentsCount);
        }
        public static IList <esriSegmentInfo> GetShortSegments(IPolycurve polycurve,
                                                               IGeometry inPerimeter,
                                                               double minimumSegmentLength,
                                                               bool use2DLengthOnly)
        {
            IGeometry projectedPerimeter = null;

            if (inPerimeter != null)
            {
                if (GeometryUtils.EnsureSpatialReference(inPerimeter, polycurve.SpatialReference,
                                                         out projectedPerimeter))
                {
                    _msg.DebugFormat("Perimeter was projected.");
                }

                if (GeometryUtils.Disjoint(polycurve, projectedPerimeter))
                {
                    return(new List <esriSegmentInfo>());
                }
            }

            bool use3DLength = GeometryUtils.IsZAware(polycurve) && !use2DLengthOnly;

            IList <esriSegmentInfo> shortSegments =
                GeometryUtils.GetShortSegments((ISegmentCollection)polycurve,
                                               minimumSegmentLength, use3DLength,
                                               projectedPerimeter);

            if (projectedPerimeter != inPerimeter)
            {
                Marshal.ReleaseComObject(projectedPerimeter);
            }

            return(shortSegments);
        }
        /// <summary>
        /// Removes the shorts segments of the specified featureVertexInfo unless they are protected
        /// by the specified featureVertexInfo's CrackPoints. The minimum of the featureVertexInfo must
        /// be set.
        /// </summary>
        /// <param name="fromPolycurve"></param>
        /// <param name="featureVertexInfo"></param>
        /// <param name="use2DLengthOnly"></param>
        /// <param name="inPerimeter"></param>
        /// <returns></returns>
        private static void RemoveShortSegments(
            [NotNull] IPolycurve fromPolycurve,
            [NotNull] FeatureVertexInfo featureVertexInfo,
            bool use2DLengthOnly,
            [CanBeNull] IGeometry inPerimeter)
        {
            Assert.ArgumentNotNull(fromPolycurve, nameof(fromPolycurve));
            Assert.ArgumentNotNull(featureVertexInfo, nameof(featureVertexInfo));
            Assert.ArgumentCondition(featureVertexInfo.ShortSegments != null,
                                     "featureVertexInfo's ShortSegments is null");
            Assert.ArgumentCondition(featureVertexInfo.MinimumSegmentLength != null,
                                     "featureVertexInfo's MinimumSegmentLength is null");

            var notifications = new NotificationCollection();

            Assert.NotNull(featureVertexInfo.MinimumSegmentLength,
                           "Minimum segment length not set.");
            var minimumSegmentLength = (double)featureVertexInfo.MinimumSegmentLength;

            IList <esriSegmentInfo> shortSegments = featureVertexInfo.ShortSegments;

            SegmentReplacementUtils.RemoveShortSegments(fromPolycurve, shortSegments,
                                                        minimumSegmentLength, use2DLengthOnly,
                                                        featureVertexInfo.CrackPointCollection,
                                                        inPerimeter, notifications);

            if (notifications.Count > 0)
            {
                _msg.WarnFormat("Feature {0}: {1}",
                                GdbObjectUtils.ToString(featureVertexInfo.Feature),
                                notifications.Concatenate(" "));
            }
        }
        public static IList <Linestring> PrepareIntersectingLinestrings(
            [NotNull] IPolycurve polycurve,
            [CanBeNull] IEnvelope aoi,
            double tolerance)
        {
            IEnumerable <IPath> paths = GeometryUtils.GetPaths(polycurve);
            var result = new List <Linestring>();

            if (aoi == null || aoi.IsEmpty)
            {
                return(result);
            }

            IEnvelope envelope = new EnvelopeClass();

            int totalPoints = GeometryUtils.GetPointCount(polycurve);

            foreach (IPath path in paths)
            {
                path.QueryEnvelope(envelope);

                if (GeometryUtils.Disjoint(aoi, envelope, tolerance))
                {
                    continue;
                }

                int        useSpatialIndexThreshold = totalPoints > 300 ? 0 : int.MaxValue;
                Linestring linestring = CreateLinestring(path, useSpatialIndexThreshold);

                result.Add(linestring);
            }

            return(result);
        }
        /// <summary>
        /// Prepare the geometry (or a copy of itself) to be serialized. Depending on <see cref="GeoJsonSerializerSettings"/>,
        /// the geometry might be altered, cloned and generalized by this function.
        ///
        /// <see cref="IGeometry"/> operations like <see cref="IGeometry.Project(ISpatialReference)"/> can
        /// have side effets (altering the input object). If <c>true</c>, geometries will not be cloned,
        /// increasing performance, if <c>false</c>, no side effects will happen, at a cost of lower
        /// performance.
        /// </summary>
        protected virtual IGeometry PrepareGeometry(IPolycurve value)
        {
            if (value == null)
            {
                return(null);
            }

            var hasNonLinearSegments = false;

            ((ISegmentCollection)value).HasNonLinearSegments(ref hasNonLinearSegments);

            var geometry = !_serializerSettings.SerializerHasSideEffects && _serializerSettings.Simplify &&
                           hasNonLinearSegments
                ? (IPolycurve)((IClone)value).Clone()
                : value;

            if (_serializerSettings.Simplify)
            {
                var topo = (ITopologicalOperator2)geometry;
                topo.IsKnownSimple_2 = false;
                topo.Simplify();

                geometry.Generalize(_serializerSettings.Tolerance);
            }
            else if (hasNonLinearSegments)
            {
                // TODO: When Simplify = false: we should not generalize the entire geometry, just its curved parts.
                // We should do this, because Generalize will return only a subset of points if they
                // fit in the tolerance given (no matter if it's a line segment or a curve).
                geometry.Generalize(_serializerSettings.Tolerance);
            }

            return(geometry);
        }
Beispiel #10
0
        private IList <IGeometry> CalculateOverlappingGeometries(
            IPolycurve sourceGeometry,
            IPolycurve overlapPolycurve)
        {
            esriGeometryDimension resultDimension =
                sourceGeometry.GeometryType == esriGeometryType.esriGeometryPolyline
                                        ? esriGeometryDimension.esriGeometry1Dimension
                                        : esriGeometryDimension.esriGeometry2Dimension;

            var intersection = (IPolycurve)
                               ((ITopologicalOperator)sourceGeometry).Intersect(overlapPolycurve,
                                                                                resultDimension);

            GeometryUtils.Simplify(intersection);

            if (intersection.IsEmpty)
            {
                return(new List <IGeometry>(0));
            }

            return(_explodeMultipartResult && IsMultipart(intersection)
                                       ? GeometryUtils.Explode(intersection)
                                       : new List <IGeometry> {
                intersection
            });
        }
Beispiel #11
0
        public void CanFindDifferingParts()
        {
            IFeatureClass featureClass = CreateLineClass(TestWorkspace);

            IPolycurve line1 =
                CurveConstruction.StartLine(0, 0, 5)
                .LineTo(1, 0, 6)
                .LineTo(1.5, 0, 6)
                .LineTo(2, 0, 6)
                .LineTo(3, 0, 7)
                .LineTo(3.2, 0, 6.8)
                .LineTo(3.5, 0, 6.5)
                .LineTo(4, 0, 6)
                .LineTo(5, 0, 7)
                .LineTo(6, 0, 7)
                .Curve;
            IFeature row1 = featureClass.CreateFeature();

            row1.Shape = line1;
            row1.Store();

            var test = new QaMonotonicZ(featureClass)
            {
                AllowConstantValues  = false,
                ExpectedMonotonicity = MonotonicityDirection.Increasing
            };
            var runner = new QaTestRunner(test);

            runner.Execute(row1);
            Assert.AreEqual(3, runner.Errors.Count);
        }
        public CurveConstruction([NotNull] IPolycurve emptyCurve,
                                 [NotNull] IPoint startPoint)
        {
            ((IZAware)emptyCurve).ZAware = ((IZAware)startPoint).ZAware;

            Curve           = emptyCurve;
            Curve.FromPoint = startPoint;
        }
Beispiel #13
0
 public PolycurveInUnionReshapeInfo(IPolycurve geometryToReshape,
                                    int geometryPartToReshape,
                                    IPath sourceReplacementPath)
 {
     GeometryToReshape     = geometryToReshape;
     GeometryPartToReshape = geometryPartToReshape;
     SourceReplacementPath = sourceReplacementPath;
 }
        private void TestPolycurve(IPolycurve errorPolycurve,
                                   ICollection <IPolyline> expectedErrorLines,
                                   double minZValue,
                                   double maxZValue)
        {
            var expectedErrorLinesCopy = new List <IPolyline>(expectedErrorLines);

            _featureClassNumber++;
            string featureClassName = string.Format("linetest_{0}", _featureClassNumber);

            IFeatureClass featureClass =
                TestWorkspaceUtils.CreateSimpleFeatureClass(
                    _testWs, featureClassName, null,
                    errorPolycurve.GeometryType,
                    esriSRProjCS2Type.esriSRProjCS_CH1903Plus_LV95, 0d,
                    true);

            IFeature row = featureClass.CreateFeature();

            row.Shape = errorPolycurve;
            row.Store();

            IList <IGeometry> errorGeometries = RunTest(featureClass, minZValue, maxZValue,
                                                        expectedErrorLines.Count);

            Assert.AreEqual(expectedErrorLines.Count, errorGeometries.Count);

            foreach (IGeometry errorGeometry in errorGeometries)
            {
                foreach (IPolyline expectedErrorGeometry in expectedErrorLinesCopy)
                {
                    if (GeometryUtils.AreEqual(errorGeometry, expectedErrorGeometry))
                    {
                        expectedErrorLinesCopy.Remove(expectedErrorGeometry);
                        break;
                    }
                }
            }

            if (expectedErrorLinesCopy.Count != 0)
            {
                Console.WriteLine(@"Expected error lines not reported:");
                foreach (IPolyline polyline in expectedErrorLinesCopy)
                {
                    Console.WriteLine(@"Expected error line:");
                    Console.WriteLine(GeometryUtils.ToString(polyline));
                }

                Console.WriteLine(@"Reported error lines:");
                foreach (IGeometry errorGeometry in errorGeometries)
                {
                    Console.WriteLine(@"Reported error line:");
                    Console.WriteLine(GeometryUtils.ToString(errorGeometry));
                }
            }

            Assert.AreEqual(0, expectedErrorLinesCopy.Count);
        }
Beispiel #15
0
        private void simpleButton1_Click(object sender, EventArgs e)
        {
            double offsetValue = -1;

            try
            {
                offsetValue = Convert.ToDouble(textEdit1.Text);
            }
            catch (Exception ex)
            {
                XtraMessageBox.Show("请输入数值类型", "提示信息", MessageBoxButtons.OK);
                return;
            }
            try
            {
                //启动编辑
                IFeatureLayer featureLayer  = mLayer as IFeatureLayer;
                IFeatureClass pFeatureClass = featureLayer.FeatureClass;

                IWorkspace    workspace     = null;
                IEngineEditor mEngineEditor = mEngineEditor = new EngineEditorClass();
                if (pFeatureClass.FeatureDataset != null)
                {
                    workspace = pFeatureClass.FeatureDataset.Workspace;
                    mEngineEditor.EditSessionMode = esriEngineEditSessionMode.esriEngineEditSessionModeVersioned;
                    mEngineEditor.StartEditing(workspace, mMap);
                    ((IEngineEditLayers)mEngineEditor).SetTargetLayer(featureLayer, -1);
                    mEngineEditor.StartOperation();
                }



                ISelectionSet mSelectionSet = (mLayer as IFeatureSelection).SelectionSet;
                ICursor       mCursor;
                mSelectionSet.Search(null, false, out mCursor);

                IFeature mFeature = mCursor.NextRow() as IFeature;
                while (mFeature != null)
                {
                    IGeometry  geometry  = mFeature.ShapeCopy;
                    IPolycurve polycurve = geometry as IPolycurve;
                    polycurve.Smooth(offsetValue);
                    mFeature.Shape = polycurve as IGeometry;
                    mFeature.Store();
                    mFeature = mCursor.NextRow() as IFeature;
                }
                if (workspace != null)
                {
                    mEngineEditor.StopEditing(true);
                }

                this.Dispose();
            }
            catch (Exception ex)
            {
                XtraMessageBox.Show("平滑失败", "提示信息", MessageBoxButtons.OK);
            }
        }
 public static IList <Linestring> PrepareIntersectingPaths(
     [NotNull] IPolycurve polycurve,
     int createSpatialIndexThreshold)
 {
     return(PrepareIntersectingPaths(
                // ReSharper disable once RedundantEnumerableCastCall
                GeometryUtils.GetPaths(polycurve).Cast <IGeometry>(),
                createSpatialIndexThreshold));
 }
Beispiel #17
0
        private void WritePolygon(IPolycurve polycurve, Ordinates ordinates)
        {
            WriteWkbType(WkbGeometryType.Polygon, ordinates);

            // Starting with OGC 1.2.0, the orientation is counter-clockwise for exterior rings:
            bool reversePointOrder = polycurve is IPolygon && ReversePolygonWindingOrder;

            WriteLineStringsCore(GetPartsAsPointLists(polycurve).ToList(), ordinates,
                                 reversePointOrder);
        }
        public void SegmentsLinearToAnglePerformance()
        {
            IPolycurve line = CurveConstruction.StartLine(10, 10).LineTo(11, 15).Curve;

            for (var i = 0; i < _segmentPerformanceCount; i++)
            {
                double angle;
                TopologicalLineUtils.CalculateToAngle((ISegmentCollection)line, out angle);
            }
        }
Beispiel #19
0
        private IMultipoint CalculateSourceIntersections(
            [NotNull] IEnumerable <IPolycurve> sourceGeometries)
        {
            IMultipoint sourceIntersectionPoints = null;
            IPolyline   sourceIntersectionLines  = null;

            foreach (
                KeyValuePair <IPolycurve, IPolycurve> pair in
                CollectionUtils.GetAllTuples(sourceGeometries))
            {
                IPolycurve polycurve1 = pair.Key;
                IPolycurve polycurve2 = pair.Value;

                IPolyline intersectionLines =
                    GeometryFactory.CreateEmptyPolyline(polycurve1);

                IMultipoint intersectionPoints =
                    IntersectionUtils.GetIntersectionPoints(
                        polycurve1, polycurve2, false,
                        IntersectionPointOptions.IncludeLinearIntersectionEndpoints,
                        intersectionLines);

                if (sourceIntersectionPoints == null)
                {
                    sourceIntersectionPoints = intersectionPoints;
                }
                else
                {
                    ((IPointCollection)sourceIntersectionPoints).AddPointCollection(
                        (IPointCollection)intersectionPoints);
                }

                if (intersectionLines != null && !intersectionLines.IsEmpty)
                {
                    if (sourceIntersectionLines == null)
                    {
                        sourceIntersectionLines = intersectionLines;
                    }
                    else
                    {
                        ((IGeometryCollection)sourceIntersectionLines).AddGeometryCollection(
                            (IGeometryCollection)intersectionLines);
                    }
                }
            }

            Assert.NotNull(sourceIntersectionPoints);

            GeometryUtils.Simplify(sourceIntersectionPoints);

            // un-simplified!
            _sourceIntersectionLines = sourceIntersectionLines;

            return(sourceIntersectionPoints);
        }
Beispiel #20
0
        public void CanTestDefinedExtentMultipleTilesWithLinesNearTileBoundary()
        {
            IPolycurve leftOfTileBoundary = CurveConstruction.StartLine(99.5, 10)
                                            .LineTo(99.5, 90)
                                            .Curve;
            IPolycurve rightOfTileBoundary = CurveConstruction.StartLine(100.5, 10)
                                             .LineTo(100.5, 90)
                                             .Curve;

            IFeatureClass fc = CreateLineClass(_testWs, "CanTestDefinedExtentMultipleTiles_fc");

            IFeature row = fc.CreateFeature();

            row.Shape = rightOfTileBoundary;
            row.Store();

            IFeatureClass nearClass = CreateLineClass(_testWs,
                                                      "CanTestDefinedExtentMultipleTiles_near");

            IFeature nearRow = nearClass.CreateFeature();

            nearRow.Shape = leftOfTileBoundary;
            nearRow.Store();

            IPolygon polygon = GeometryFactory.CreatePolygon(
                GeometryFactory.CreateEnvelope(0, 0, 200, 200));

            const double maximumDistance = 1.5;

            // one tile for the entire verification extent
            QaContainerTestRunner runnerLargeTileSize = CreateTestRunner(
                fc, nearClass, 200, maximumDistance);

            runnerLargeTileSize.Execute(polygon);

            AssertUtils.NoError(runnerLargeTileSize);

            // 4 tiles.
            // The feature is in the second tile, to the right of the left tile boundary, but within the search distance of it.
            // The 'near feature' is in the first tile, to the left of the right tile boundary, but within the search distance of it.

            // NOTE currently this results in an error, since the 'near feature' is not returned again for the second tile
            // (the test container assumes that it was already considered in the first tile; in this case however, it is
            // needed for a feature in the *second* tile, which was not yet returned for the first tile.

            // -> if the search geometry overlaps the tile boundary but the source feature for the search DOES NOT, then
            //    features must be returned by the search even if they overlap a previous tile
            QaContainerTestRunner runnerSmallTileSize = CreateTestRunner(
                fc, nearClass, 100, maximumDistance);

            runnerSmallTileSize.Execute(polygon);

            AssertUtils.NoError(runnerSmallTileSize);
        }
        public static MultiPolycurve CreateMultiPolycurve([NotNull] IPolycurve polycurve,
                                                          double tolerance          = 0,
                                                          [CanBeNull] IEnvelope aoi = null)
        {
            // Note: Getting the paths from the GeometryCollection takes a large percentage of the entire method.
            //       However, figuring out the point count per part and extracting a sub-sequence of the array
            //       is not faster. Most likely because unpacking the coordinates has to take place anyway.
            IEnumerable <IPath> paths = GeometryUtils.GetPaths(polycurve);

            return(CreateMultiPolycurve(paths, tolerance, aoi));
        }
Beispiel #22
0
        private static IGeometry GetDifference(IPolycurve fromGeometry,
                                               IPolycurve overlappingGeometry)
        {
            GeometryUtils.AllowIndexing(fromGeometry);

            IGeometry result =
                ((ITopologicalOperator)fromGeometry).Difference(overlappingGeometry);

            GeometryUtils.EnsureSpatialReference(result, fromGeometry.SpatialReference);
            GeometryUtils.EnsureSimple(result);
            return(result);
        }
        private static IGeometry GetErrorGeometry([NotNull] ICurve shape)
        {
            // TODO use envelope if vertex count is too big?
            // return shape.Envelope;

            IPolycurve polycurve = CreateHighLevelCopy(shape);

            const int maxAllowableOffsetFactor = 10;

            polycurve.Weed(maxAllowableOffsetFactor);

            return(polycurve);
        }
        public void SegmentLinearTangentPerformance()
        {
            IPolycurve line = CurveConstruction.StartLine(10, 10)
                              .LineTo(11, 15)
                              .Curve;
            ISegment segment = ((ISegmentCollection)line).Segment[0];
            ILine    tangent = new LineClass();

            for (var i = 0; i < _segmentPerformanceCount; i++)
            {
                segment.QueryTangent(esriSegmentExtension.esriExtendAtFrom, 0, false, 1, tangent);
            }
        }
Beispiel #25
0
        public void InsertIntersectingVerticesInTargets(
            [NotNull] IEnumerable <IFeature> targetFeatures,
            [NotNull] IPolycurve removeGeometry)
        {
            if (Result.TargetFeaturesToUpdate == null)
            {
                Result.TargetFeaturesToUpdate = new Dictionary <IFeature, IGeometry>();
            }

            ReshapeUtils.InsertIntersectingVerticesInTargets(
                targetFeatures, removeGeometry,
                Result.TargetFeaturesToUpdate);
        }
        public void SegmentWksFromPointPerformance()
        {
            IPolycurve line = CurveConstruction.StartLine(10, 10)
                              .LineTo(11, 15)
                              .Curve;
            ISegment segment = ((ISegmentCollection)line).Segment[0];

            for (var i = 0; i < _segmentPerformanceCount; i++)
            {
                WKSPoint p;
                segment.QueryWKSFromPoint(out p);
            }
        }
Beispiel #27
0
 /// <summary>
 /// This function will return a point collection for intersections along a curve
 /// </summary>
 /// <param name="inFeature">the feature to intersect</param>
 /// <param name="inTopOp">the curve to use to intersect the feature</param>
 /// <returns>a point collection of intersecting points along the infeature</returns>
 public static IPointCollection PerformTopologicalIntersect(IFeature inFeature, ITopologicalOperator inTopOp)
 {
     try
     {
         IPolycurve  pFeatureShape = inFeature.Shape as IPolycurve;
         IMultipoint pIntersection =
             (IMultipoint)inTopOp.Intersect(pFeatureShape, esriGeometryDimension.esriGeometry0Dimension);
         return((IPointCollection)pIntersection);
     }
     catch (Exception ex)
     {
         throw new Exception(ex.Message);
     }
 }
        public void SegmentNonLinearToAnglePerformance()
        {
            IPolycurve line = CurveConstruction.StartLine(10, 10)
                              .BezierTo(11, 12, 11, 13, 12, 15)
                              .Curve;

            for (var i = 0; i < _segmentPerformanceCount; i++)
            {
                var      segs = (ISegmentCollection)line;
                ISegment seg  = segs.Segment[segs.SegmentCount - 1];
                double   angle;
                TopologicalLineUtils.CalculateToAngle(seg, out angle);
            }
        }
        public void SegmentFromPointPerformance()
        {
            IPolycurve line = CurveConstruction.StartLine(10, 10)
                              .LineTo(11, 15)
                              .Curve;
            ISegment segment = ((ISegmentCollection)line).Segment[0];

            for (var i = 0; i < _segmentPerformanceCount; i++)
            {
                double x, y;
                IPoint f = segment.FromPoint;
                f.QueryCoords(out x, out y);
            }
        }
        private void CompareSegmentAnglesCore([NotNull] IPolycurve sourceShape,
                                              [NotNull] IPolycurve transformedShape,
                                              [NotNull] IFeature transformedFeature)
        {
            var sourceParts      = (IGeometryCollection)sourceShape;
            var transformedParts = (IGeometryCollection)transformedShape;

            int sourcePartCount = sourceParts.GeometryCount;

            for (var index = 0; index < sourcePartCount; index++)
            {
                CompareSegmentAngles((IPath)sourceParts.Geometry[index],
                                     (IPath)transformedParts.Geometry[index],
                                     transformedFeature);
            }
        }
        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 RelativeOrientation GetRelativeOrientation(IPolycurve pFoundLineAsPolyCurve, IPolycurve inPolycurve)
        {
            //check to see if the segments overlap
            double line_to = ((IProximityOperator)pFoundLineAsPolyCurve).ReturnDistance(inPolycurve.ToPoint);
            double line_from = ((IProximityOperator)pFoundLineAsPolyCurve).ReturnDistance(inPolycurve.FromPoint);

            if (line_to < 0.005 && line_from < 0.005)
            {
                //if they do, find the relative orientation and then return Same or Reverse
                RelativeOrientation orientation = GetRelativeOrientation_ignoreSame(pFoundLineAsPolyCurve, inPolycurve);
                if (orientation == RelativeOrientation.To_To || orientation == RelativeOrientation.From_From)
                    return RelativeOrientation.Same;
                if (orientation == RelativeOrientation.From_To || orientation == RelativeOrientation.To_From)
                    return RelativeOrientation.Reverse;

                throw new Exception("Invalid relative orientation for overlapping segments returend");
            }
            //if they don't, just get the relative orientation
            return GetRelativeOrientation_ignoreSame(pFoundLineAsPolyCurve, inPolycurve);
        }
        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;
        }
        public bool HasParallelCurveMatchFeatures(IFeatureClass FeatureClass, IPolycurve inPolycurve, string WhereClause,
      double AngleToleranceTangentCompareInDegrees, double OrthogonalSearchDistance,
       out int outFoundLinesCount, out int outFoundParallelCurvesCount, ref List<string> CurveInfoFromNeighbours)
        {
            outFoundLinesCount = 0;
              outFoundParallelCurvesCount = 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 pNormalLine = new Line(); //new
              for (int i = -1; i < 2; i = i + 2)
              {
            double dOffset = OrthogonalSearchDistance * i;

            inPolycurve.QueryNormal(esriSegmentExtension.esriNoExtension, 0.5, true, dOffset, pNormalLine);
            ILine pThisLine = new Line();

            pThisLine.PutCoords(pNormalLine.FromPoint, pNormalLine.ToPoint);
            pGeomColl.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 = new PathClass();
            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.
            geometryCollection2.AddGeometry(newPath as IGeometry, ref obj, ref obj);
              }

              ISpatialFilter pSpatFilt = new SpatialFilter();
              pSpatFilt.WhereClause = WhereClause;
              pSpatFilt.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;
              pSpatFilt.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;

              pSpatFilt.Geometry = pGeomBag;

              IFeatureCursor pFeatCursLines = null;
              try
              {
            pFeatCursLines = FeatureClass.Search(pSpatFilt, false);
              }
              catch (Exception ex)
              {
            MessageBox.Show(ex.Message);
            return false;
              }

              IFeature pFeat = pFeatCursLines.NextFeature();
              while (pFeat != null)
              {
            IGeometry pFoundLineGeom = pFeat.ShapeCopy;

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

            if (dRadius == 0)
            {//null or zero radius so skip.
              Marshal.ReleaseComObject(pFeat);
              pFeat = pFeatCursLines.NextFeature();
              continue;
            }

            val = pFeat.get_Value(idxCenterPointID);
            if (val == DBNull.Value)
            {//null centrpointID so skip.
              Marshal.ReleaseComObject(pFeat);
              pFeat = pFeatCursLines.NextFeature();
              continue;
            }

            iCtrPoint = (int)val;

            ITopologicalOperator6 pTopoOp6 = (ITopologicalOperator6)MultiPartPolyLine;
            IGeometry pResultGeom = pTopoOp6.IntersectEx(pFoundLineGeom, false, esriGeometryDimension.esriGeometry0Dimension);
            if (pResultGeom == null)
            {
              Marshal.ReleaseComObject(pFeat);
              pFeat = pFeatCursLines.NextFeature();
              continue;
            }
            if (pResultGeom.IsEmpty)
            {
              Marshal.ReleaseComObject(pFeat);
              pFeat = pFeatCursLines.NextFeature();
              continue;
            }

            ISegmentCollection pFoundLineGeomSegs = pFoundLineGeom as ISegmentCollection;
            bool bHasCurves = false;
            pFoundLineGeomSegs.HasNonLinearSegments(ref bHasCurves);
            if (!bHasCurves)
            {
              Marshal.ReleaseComObject(pFeat);
              pFeat = pFeatCursLines.NextFeature();
              continue;
            }

            IPointCollection5 PtColl = (IPointCollection5)pResultGeom;

            if (PtColl.PointCount > 1)
            {
              Marshal.ReleaseComObject(pFeat);
              pFeat = pFeatCursLines.NextFeature();
              continue;
            }
            IPolycurve pPolyCurve4Tangent = pFoundLineGeom as IPolycurve;

            for (int j = 0; j < PtColl.PointCount; j++)
            {
              IPoint p = PtColl.get_Point(j);
              IPoint outPoint = 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 = 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 = new Vector3DClass();
              vecTangent.PolarSet(pTangent.Angle, 0, 1);

              IVector3D vecNormal = new Vector3DClass();
              vecNormal.PolarSet(pNormalLine.Angle, 0, 1);

              ILine pHitDistanceForRadiusDifference = 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 < AngleToleranceTangentCompareInDegrees)
              {
            //work out concavity orientation with respect to the original line using the radius sign and dot product
            dDotProd = vecOriginalSelected.DotProduct(vecTangent);
            double dTangentCheck = Math.Acos(dDotProd) * 180 / Math.PI; // in degrees
            //dTangentCheck at this point should be close to 0 or 180 degrees.
            outFoundLinesCount++;

            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") + "," +
              iCtrPoint.ToString() + "," + dRadiusDiff.ToString("#.000");
            CurveInfoFromNeighbours.Add(sHarvestedCurveInfo);
              }
            }

            Marshal.ReleaseComObject(pFeat);
            pFeat = pFeatCursLines.NextFeature();
              }
              Marshal.FinalReleaseComObject(pFeatCursLines);

              bool bHasParallelCurveFeaturesNearby = (outFoundLinesCount > 0);

              return bHasParallelCurveFeaturesNearby;
        }
        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;
        }
        private RelativeOrientation GetRelativeOrientation_org(IPolycurve pFoundLineAsPolyCurve, IPolycurve 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

            Dictionary<int, double> dictSort2GetShortest = new Dictionary<int, double>();

            ILine pFoundTo_2_OriginalTo = new Line();
            pFoundTo_2_OriginalTo.PutCoords(pFoundLineAsPolyCurve.ToPoint, inPolycurve.ToPoint);
            dictSort2GetShortest.Add(1, pFoundTo_2_OriginalTo.Length);

            ILine pFoundFrom_2_OriginalTo = new Line();
            pFoundFrom_2_OriginalTo.PutCoords(pFoundLineAsPolyCurve.FromPoint, inPolycurve.ToPoint);
            dictSort2GetShortest.Add(2, pFoundFrom_2_OriginalTo.Length);

            ILine pFoundTo_2_OriginalFrom = new Line();
            pFoundTo_2_OriginalFrom.PutCoords(pFoundLineAsPolyCurve.ToPoint, inPolycurve.FromPoint);
            dictSort2GetShortest.Add(3, pFoundTo_2_OriginalFrom.Length);

            ILine pFoundFrom_2_OriginalFrom = new Line();
            pFoundFrom_2_OriginalFrom.PutCoords(pFoundLineAsPolyCurve.FromPoint, inPolycurve.FromPoint);
            dictSort2GetShortest.Add(4, pFoundFrom_2_OriginalFrom.Length);

            var sortedDict = from entry in dictSort2GetShortest orderby entry.Value ascending select entry;
            var pEnum = sortedDict.GetEnumerator();
            pEnum.MoveNext(); //get the first key for the shortest line
            var pair = pEnum.Current;
            int iOpt = pair.Key;
            return (RelativeOrientation)iOpt;
        }
        private RelativeOrientation GetRelativeOrientation2(IPolycurve pFoundLineAsPolyCurve, IPolycurve 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
            
            Dictionary<int, double> dictSort2GetShortest = new Dictionary<int, double>();
            dictSort2GetShortest.Add(1, ((IProximityOperator)pFoundLineAsPolyCurve.ToPoint).ReturnDistance(inPolycurve.ToPoint));
            dictSort2GetShortest.Add(2, ((IProximityOperator)pFoundLineAsPolyCurve.FromPoint).ReturnDistance(inPolycurve.ToPoint));
            dictSort2GetShortest.Add(3, ((IProximityOperator)pFoundLineAsPolyCurve.ToPoint).ReturnDistance(inPolycurve.FromPoint));
            dictSort2GetShortest.Add(4, ((IProximityOperator)pFoundLineAsPolyCurve.FromPoint).ReturnDistance(inPolycurve.FromPoint));

            return (RelativeOrientation)dictSort2GetShortest.Aggregate((l, r) => l.Value < r.Value ? l : r).Key;
        }
        private RelativeOrientation GetRelativeOrientation_ignoreSame(IPolycurve pFoundLineAsPolyCurve, IPolycurve 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

            RelativeOrientation ret = RelativeOrientation.To_To;
            double min = 0;

            double To_To = min = ((IProximityOperator)pFoundLineAsPolyCurve.ToPoint).ReturnDistance(inPolycurve.ToPoint);
            if (To_To < 0.005)
                return RelativeOrientation.To_To;


            double From_From = ((IProximityOperator)pFoundLineAsPolyCurve.FromPoint).ReturnDistance(inPolycurve.FromPoint);
            if (From_From < 0.005)
            {
                return RelativeOrientation.From_From;
            }
            else if (From_From < To_To)
            {
                min = From_From;
                ret = RelativeOrientation.From_From;
            }


            double From_To = ((IProximityOperator)pFoundLineAsPolyCurve.ToPoint).ReturnDistance(inPolycurve.FromPoint);
            if (From_To < 0.005)
            {
                return RelativeOrientation.From_To;
            }
            if (From_To < min)
            {
                ret = RelativeOrientation.From_To;
                min = From_To;
            }


            double To_From = ((IProximityOperator)pFoundLineAsPolyCurve.FromPoint).ReturnDistance(inPolycurve.ToPoint);
            if (To_From < min)
            {
                ret = RelativeOrientation.To_From;
            }

            return (RelativeOrientation)ret;
        }
        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;
        }