public IEnumerable <CutSubcurve> ConnectedSubcurvesFromLeftToRight( CutSubcurve startCurve) { _connectedSubcurves.Sort(new CutSubcurveComparer(startCurve, this, false)); return(_connectedSubcurves); }
public bool TryJoinNonForkingNeighbourCandidates( [NotNull] CutSubcurve subcurve, [NotNull] List <CutSubcurve> replacedSubcurves, out CutSubcurve result) { if (!subcurve.IsReshapeMemberCandidate || subcurve.IsFiltered) { result = null; return(false); } result = subcurve; if (!result.TouchAtFromPoint) { TryMergeWithSingleAdjacentCandidate(ref result, subcurve.FromNode, replacedSubcurves); } if (!result.TouchAtToPoint) { TryMergeWithSingleAdjacentCandidate(ref result, subcurve.ToNode, replacedSubcurves); } return(result != subcurve); }
private static bool FullyWithinAnyExtent(IPolyline highLevelPath, CutSubcurve cutSubcurve, IEnumerable <IEnvelope> extents) { foreach (IEnvelope extent in extents) { if (extent.IsEmpty) { continue; } if (GeometryUtils.Contains(extent, highLevelPath)) { if (!cutSubcurve.CanReshape && (GeometryUtils.Touches(extent, highLevelPath.FromPoint) || GeometryUtils.Touches(extent, highLevelPath.ToPoint))) { // it was cut off by the extent continue; } return(true); } } return(false); }
private static SubcurveNode GetNodeAt(IPoint checkPoint, CutSubcurve cutSubcurve1, CutSubcurve cutSubcurve2, out bool isTouchingSource) { SubcurveNode result; if (GeometryUtils.AreEqualInXY(checkPoint, cutSubcurve1.Path.FromPoint)) { result = cutSubcurve1.FromNode; isTouchingSource = cutSubcurve1.TouchAtFromPoint; } else if (GeometryUtils.AreEqualInXY(checkPoint, cutSubcurve1.Path.ToPoint)) { result = cutSubcurve1.ToNode; isTouchingSource = cutSubcurve1.TouchAtToPoint; } else if (GeometryUtils.AreEqualInXY(checkPoint, cutSubcurve2.Path.FromPoint)) { result = cutSubcurve2.FromNode; isTouchingSource = cutSubcurve2.TouchAtFromPoint; } else { Assert.True( GeometryUtils.AreEqualInXY(checkPoint, cutSubcurve2.Path.ToPoint), "Unexpected situation after merging adjacent subcurves."); result = cutSubcurve2.ToNode; isTouchingSource = cutSubcurve2.TouchAtToPoint; } return(result); }
private static bool IsConnected(CutSubcurve subcurve, SubcurveNode atNode, ICollection <SubcurveNode> checkedNodes, ICollection <CutSubcurve> intermediateCurves, Predicate <CutSubcurve> connectCondition) { // TODO: cache the thisSubcurve on the subcurve to improve performance if (_msg.IsVerboseDebugEnabled) { _msg.DebugFormat("Checking if connected at {0}/{1}", atNode.X, atNode.Y); } if (connectCondition != null && !connectCondition(subcurve)) { return(false); } if ((atNode == subcurve.FromNode && subcurve.TouchAtFromPoint) || (atNode == subcurve.ToNode && subcurve.TouchAtToPoint)) { checkedNodes.Add(atNode); intermediateCurves.Add(subcurve); return(true); } if (checkedNodes.Contains(atNode)) { return(false); } // block the node to avoid circles checkedNodes.Add(atNode); // NOTE: sometimes reshape candidates are not found because the node traversal cuts off the other // node's path (and vice versa when the second starts first): // TODO: always try the left-most / or for the other node the right-most subcurve (invert in second run) // to avoid cutting the other node's path -> this is not the perfect solution either (debug) // TODO: minimal but robust solution: make sure both ends are connected regardless of the other's path (only exclude other node) //IEnumerable<CutSubcurve> leaves = traverseRightCurvesFirst // ? atNode.ConnectedSubcurvesFromRightToLeft(subcurve) // : atNode.ConnectedSubcurvesFromLeftToRight(subcurve); foreach (CutSubcurve connectedSubcurve in atNode.ConnectedSubcurves) { if (connectedSubcurve != subcurve && IsConnected(connectedSubcurve, connectedSubcurve.OtherNode(atNode), checkedNodes, intermediateCurves, connectCondition)) { intermediateCurves.Add(subcurve); return(true); } } return(false); }
public bool IsExcluded(CutSubcurve cutSubcurve) { var highLevelPath = (IPolyline)GeometryUtils.GetHighLevelGeometry(cutSubcurve.Path, true); if (_currentlyVisibleExtents != null) { if (!FullyWithinAnyExtent(highLevelPath, cutSubcurve, _currentlyVisibleExtents)) { return(true); } } if (_filterOptions.ExcludeOutsideSource) { var mustInteriorIntersectPoly = _currentSourceGeometry as IPolygon; if (mustInteriorIntersectPoly != null && !GeometryUtils.InteriorIntersects( mustInteriorIntersectPoly, highLevelPath)) { return(true); } } if (_mustBeWithinSourceBuffer != null) { if (!GeometryUtils.Contains(_mustBeWithinSourceBuffer, highLevelPath)) { return(true); } } if (_filterOptions.ExcludeResultingInOverlaps && _targetUnionPoly != null && _sourceTargetPolyUnionBoundary != null) { // Avoid Error in Disjoint/Contains: Spatial References of provided geometries are not consistent. // This happens sometimes, probably due to map SR different to data SR. // But also with minimum tolerance: the subcurves are calculated in minimum tolerance GeometryUtils.EnsureSpatialReference(_targetUnionPoly, highLevelPath.SpatialReference); GeometryUtils.EnsureSpatialReference(_sourceTargetPolyUnionBoundary, highLevelPath.SpatialReference); if (ResultsInOverlaps(highLevelPath, _targetUnionPoly, _sourceTargetPolyUnionBoundary)) { return(true); } } Marshal.ReleaseComObject(highLevelPath); return(false); }
private static void TryMergeWithSingleAdjacentCandidate( ref CutSubcurve thisSubcurve, SubcurveNode inNode, List <CutSubcurve> replacedSubcurves) { CutSubcurve singleConnectedCandidataCurve = null; foreach (CutSubcurve adjacentSubcurve in inNode.ConnectedSubcurves) { if (adjacentSubcurve == thisSubcurve) { continue; } if (!adjacentSubcurve.IsReshapeMemberCandidate || adjacentSubcurve.IsFiltered) { // already green or red or grey: continue; } if (replacedSubcurves.Contains(adjacentSubcurve)) { continue; } if (TouchesSourceInNode(adjacentSubcurve, inNode)) { // do not merge across source lines continue; } if (singleConnectedCandidataCurve == null) { singleConnectedCandidataCurve = adjacentSubcurve; } else { // more than one - cannot merge return; } } if (singleConnectedCandidataCurve == null) { return; } // Add to replaced curves before assigning the final thisSubcurve through merging the two replacedSubcurves.Add(thisSubcurve); replacedSubcurves.Add(singleConnectedCandidataCurve); thisSubcurve = Replace(thisSubcurve, singleConnectedCandidataCurve, inNode); }
private static bool TouchesSourceInNode(CutSubcurve subcurve, SubcurveNode inNode) { if (subcurve.FromNode.Equals(inNode)) { return(subcurve.TouchAtFromPoint); } if (subcurve.ToNode.Equals(inNode)) { return(subcurve.TouchAtToPoint); } // Assert.CantReach() here? return(false); }
private static void JoinNonForkingSubcurves( ICollection <CutSubcurve> inSubcurveCollection) { var removedCurves = new List <CutSubcurve>(); var additionalCurves = new List <CutSubcurve>(); foreach (CutSubcurve reshapeSubcurve in inSubcurveCollection) { if (removedCurves.Contains(reshapeSubcurve)) { continue; } if (reshapeSubcurve.IsReshapeMemberCandidate) { // try building up a non-forking path from the non-touching end through a // set of connected subcurves back to the geometry to reshape CutSubcurve currentInputCurve = reshapeSubcurve; CutSubcurve mergedCurve; while (reshapeSubcurve.TryJoinNonForkingNeighbourCandidates( currentInputCurve, removedCurves, out mergedCurve)) { currentInputCurve = mergedCurve; } if (currentInputCurve != reshapeSubcurve) { // one or more merges happened: additionalCurves.Add(currentInputCurve); } } } foreach (CutSubcurve removedCurve in removedCurves) { inSubcurveCollection.Remove(removedCurve); } foreach (CutSubcurve additionalCurve in additionalCurves) { inSubcurveCollection.Add(additionalCurve); } }
private bool Equals(CutSubcurve other) { if (ReferenceEquals(null, other)) { return(false); } if (ReferenceEquals(this, other)) { return(true); } return (Equals(other.Source, Source) && other._touchAtFromPoint.Equals(_touchAtFromPoint) && other._touchAtToPoint.Equals(_touchAtToPoint) && (_path.Equals(other._path) || // path can be reference equal ((IClone)_path).IsEqual((IClone)other._path))); // or IClone.IsEqual }
private bool ApplyReshapePath(IGeometry geometryToReshape, IPath reshapePath, NotificationCollection notifications, [CanBeNull] CutSubcurve cutSubcurve, IDictionary <IGeometry, NotificationCollection> reshapedGeometries) { var reshapeInfo = new ReshapeInfo(geometryToReshape, reshapePath, notifications) { PartIndexToReshape = 0, // TODO CutReshapePath = cutSubcurve }; // TODO: make ReshapeSingleGeometryInUnion not depend on unionReshapeInfo and use the same general workflow? bool reshaped = ReshapeSinglePolygonInUnion(reshapeInfo); if (reshaped) { // to avoid incorrect relational operator results in for the next path on target ((ISegmentCollection)geometryToReshape).SegmentsChanged(); AddToRefreshArea(reshapeInfo); } // move adding notfications to caller? if (reshaped && !reshapedGeometries.ContainsKey(reshapeInfo.GeometryToReshape)) { reshapedGeometries.Add(reshapeInfo.GeometryToReshape, reshapeInfo.Notifications); } else { if (reshapeInfo.Notifications != null) { NotificationUtils.Add(notifications, reshapeInfo.Notifications.Concatenate(" ")); } } return(reshaped); }
private static double GetLineAngle([NotNull] CutSubcurve subcurve, SubcurveNode atNode) { var segments = (ISegmentCollection)subcurve.Path; ISegment segment; var reverseOrientation = false; if (atNode == subcurve.ToNode) { // use last segment and the line needs to be inverted segment = segments.Segment[segments.SegmentCount - 1]; reverseOrientation = true; } else { segment = segments.Segment[0]; } var line = segment as ILine; if (line == null) { line = new LineClass(); segment.QueryTangent(esriSegmentExtension.esriNoExtension, 1, true, 10, line); } double angle = line.Angle; if (reverseOrientation) { angle = angle >= Math.PI ? angle - Math.PI : angle + Math.PI; } return(angle); }
private static CutSubcurve CreateCutSubcurve( [NotNull] IPath path, [CanBeNull] IPointCollection intersectionPoints, [NotNull] IDictionary <SubcurveNode, SubcurveNode> allNodes, bool?touchingDifferentParts, IPolyline targetPolyline, double stitchPointSearchTol) { var touchAtFromPoint = false; var touchAtToPoint = false; if (intersectionPoints != null) { touchAtFromPoint = GeometryUtils.Intersects( path.FromPoint, (IGeometry)intersectionPoints); touchAtToPoint = GeometryUtils.Intersects( path.ToPoint, (IGeometry)intersectionPoints); } var fromNode = new SubcurveNode(path.FromPoint.X, path.FromPoint.Y); if (allNodes.ContainsKey(fromNode)) { fromNode = allNodes[fromNode]; } else { allNodes.Add(fromNode, fromNode); } var toNode = new SubcurveNode(path.ToPoint.X, path.ToPoint.Y); if (allNodes.ContainsKey(toNode)) { toNode = allNodes[toNode]; } else { allNodes.Add(toNode, toNode); } var cutSubcurve = new CutSubcurve(path, touchAtFromPoint, touchAtToPoint, fromNode, toNode, touchingDifferentParts); // Identify stitch points, i.e. points that do not exist in the target and should not end up in the // reshaped geometry if several adjacent cutsubcurves are applied together. if (touchAtFromPoint) { IPoint point = cutSubcurve.Path.FromPoint; ISegment targetSegment; cutSubcurve.FromPointIsStitchPoint = IsNoTargetVertex( point, targetPolyline, stitchPointSearchTol, out targetSegment); cutSubcurve.TargetSegmentAtFromPoint = targetSegment; } if (touchAtToPoint) { IPoint point = cutSubcurve.Path.ToPoint; ISegment targetSegment; cutSubcurve.ToPointIsStitchPoint = IsNoTargetVertex( point, targetPolyline, stitchPointSearchTol, out targetSegment); cutSubcurve.TargetSegmentAtToPoint = targetSegment; } fromNode.ConnectedSubcurves.Add(cutSubcurve); toNode.ConnectedSubcurves.Add(cutSubcurve); return(cutSubcurve); }
private static CutSubcurve Replace(CutSubcurve cutSubcurve1, CutSubcurve cutSubcurve2, SubcurveNode mergeNode) { IGeometryCollection mergedCollection = ReshapeUtils.GetSimplifiedReshapeCurves( new List <CutSubcurve> { cutSubcurve1, cutSubcurve2 }); Assert.AreEqual(1, mergedCollection.GeometryCount, "Unexpected number of geometries after merging adjacent subcurves"); var newPath = (IPath)mergedCollection.get_Geometry(0); bool touchAtFrom; SubcurveNode oldNodeAtFrom = GetNodeAt(newPath.FromPoint, cutSubcurve1, cutSubcurve2, out touchAtFrom); bool touchAtTo; SubcurveNode oldNodeAtTo = GetNodeAt(newPath.ToPoint, cutSubcurve1, cutSubcurve2, out touchAtTo); if (oldNodeAtFrom.ConnectedSubcurves.Contains(cutSubcurve1)) { oldNodeAtFrom.ConnectedSubcurves.Remove(cutSubcurve1); } if (oldNodeAtFrom.ConnectedSubcurves.Contains(cutSubcurve2)) { oldNodeAtFrom.ConnectedSubcurves.Remove(cutSubcurve2); } if (oldNodeAtTo.ConnectedSubcurves.Contains(cutSubcurve1)) { oldNodeAtTo.ConnectedSubcurves.Remove(cutSubcurve1); } if (oldNodeAtTo.ConnectedSubcurves.Contains(cutSubcurve2)) { oldNodeAtTo.ConnectedSubcurves.Remove(cutSubcurve2); } var result = new CutSubcurve(newPath, touchAtFrom, touchAtTo, oldNodeAtFrom, oldNodeAtTo); result.Source = cutSubcurve1.Source; oldNodeAtFrom.ConnectedSubcurves.Add(result); oldNodeAtTo.ConnectedSubcurves.Add(result); // Old target intersection points: Add them if they were not stitch points removed after simplify: IPoint connectPoint = GeometryFactory.CreatePoint(mergeNode.X, mergeNode.Y, newPath.SpatialReference); int partIdx; int?connectIndex = GeometryUtils.FindHitVertexIndex( newPath, connectPoint, GeometryUtils.GetXyTolerance(newPath), out partIdx); if (connectIndex != null) { result.AddExtraPotentialTargetInsertPoint( ((IPointCollection)newPath).get_Point((int)connectIndex)); } return(result); }