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