private bool CutFeature([NotNull] IFeature feature, [NotNull] IPolyline cutPolyline, ChangeAlongZSource?zSource = null) { IGeometry selectedGeometry = feature.Shape; if (GeometryUtils.Disjoint(cutPolyline, selectedGeometry)) { return(false); } if (FeatureToCutPredicate != null && !FeatureToCutPredicate(selectedGeometry, cutPolyline)) { return(false); } _msg.DebugFormat("Cutting feature {0}", GdbObjectUtils.ToString(feature)); IGeometry geometryToCut = GeometryFactory.Clone(selectedGeometry); CutPolyline usedCutLine = null; if (ProcessedCutLines != null) { usedCutLine = new CutPolyline(feature.OID); ProcessedCutLines.Add(usedCutLine); } IList <IGeometry> resultGeometries; switch (geometryToCut.GeometryType) { case esriGeometryType.esriGeometryPolygon: resultGeometries = CutGeometryUtils.TryCut( (IPolygon)geometryToCut, cutPolyline, zSource ?? DetermineZSource(feature)); break; case esriGeometryType.esriGeometryPolyline: resultGeometries = _networkFeatureCutter?.IsNetworkEdge(feature) == true ? GetNetworkSplitPoints(cutPolyline, geometryToCut) : CutGeometryUtils.TryCut((IPolyline)geometryToCut, cutPolyline); break; case esriGeometryType.esriGeometryMultiPatch: resultGeometries = CutGeometryUtils.TryCut((IMultiPatch)geometryToCut, cutPolyline, zSource ?? DetermineZSource(feature), usedCutLine, DegenerateMultipatchFootprintAction) .Values.Cast <IGeometry>() .ToList(); break; default: throw new InvalidOperationException( $"Unsupported geometry type: {geometryToCut.GeometryType}"); } if (resultGeometries != null && resultGeometries.Count > 0) { IList <IGeometry> previousResults; if (ResultGeometriesByFeature.TryGetValue(feature, out previousResults)) { foreach (IGeometry resultGeometry in resultGeometries) { previousResults.Add(resultGeometry); } } else { ResultGeometriesByFeature.Add(feature, resultGeometries); } } return(resultGeometries != null && resultGeometries.Count > 0); }
/// <summary> /// Cuts the provided multipatch along the specified cutLine. /// </summary> /// <param name="multipatch"></param> /// <param name="cutLine"></param> /// <param name="zSource">The source of the Z values to be used for the new vertices of the result features.</param> /// <param name="usedCutLine">The cut result containing information about the cut operation.</param> /// <param name="nonSimpleFootprintAction">The action to be performed if one of the result multipatches /// has a degenerate footprint (because it is a sliver polygon with sub-tolerance self intersections).</param> /// <returns></returns> public static IDictionary <IPolygon, IMultiPatch> TryCut( [NotNull] IMultiPatch multipatch, [NotNull] IPolyline cutLine, ChangeAlongZSource zSource, [CanBeNull] CutPolyline usedCutLine = null, DegenerateMultipatchFootprintAction nonSimpleFootprintAction = DegenerateMultipatchFootprintAction.Throw) { Assert.ArgumentNotNull(multipatch, nameof(multipatch)); Assert.ArgumentNotNull(cutLine, nameof(cutLine)); bool allRings = GeometryUtils.IsRingBasedMultipatch(multipatch); Assert.True(allRings, "The multipatch geometry contains triangles, triangle fans or triangle strips, which are currently not supported"); // NOTE: ITopologicalOperator4 is not supported by multipatch, ITopologicalOperator3.Cut returns non-Z-aware polygons // -> Use GeomUtils.Cut implementation which could eventually classify the left/right parts and avoid footprint-cutting // only to find the correct assignment to result features. // TODO: Create footprint as RingGroups directly from multipatch rings IPolygon footprint = GeometryFactory.CreatePolygon(multipatch); IList <IGeometry> cutFootprintParts = TryCutRingGroups(footprint, cutLine, ChangeAlongZSource.Target); if (cutFootprintParts == null || cutFootprintParts.Count == 0) { _msg.DebugFormat( "Not even the footprint could be cut. No multipatch cutting performed."); if (usedCutLine != null) { usedCutLine.Polyline = cutLine; usedCutLine.SuccessfulCut = false; } return(new Dictionary <IPolygon, IMultiPatch>(0)); } // Get the 'connected components', i.e. outer ring with respective inner rings. IList <GeometryPart> multipatchParts = GeometryPart.FromGeometry(multipatch).ToList(); Dictionary <RingGroup, List <RingGroup> > splitPartsByFootprintPart = PrepareSplitPartsDictionary(cutFootprintParts); foreach (GeometryPart part in multipatchParts) { CutAndAssignToFootprintParts(part, cutLine, splitPartsByFootprintPart, zSource); } // make a separate multipatch per footprint-part with all respective ring-groups Dictionary <IPolygon, IMultiPatch> result = BuildResultMultipatches( multipatch, splitPartsByFootprintPart, nonSimpleFootprintAction); if (usedCutLine != null && splitPartsByFootprintPart.Values.Count > 1) { // TODO: Extract actual cutLine from inBound/outBound intersections usedCutLine.Polyline = cutLine; usedCutLine.SuccessfulCut = false; } return(result); }
private void CutFeature3D(IFeature feature, IMultiPatch cutSurfaces) { IGeometry selectedGeometry = feature.Shape; if (GeometryUtils.Disjoint(cutSurfaces, selectedGeometry)) { return; } if (GeometryUtils.AreEqual(cutSurfaces, selectedGeometry)) { return; } if (FeatureToCutPredicate != null && !FeatureToCutPredicate(selectedGeometry, cutSurfaces)) { return; } // TODO: Multipatches should be cut if its footprint can be cut. However, if multiple ring groups exist, // that are not connected (touch along a line) in 3D, the rings should be grouped into connected parts. List <CutPolyline> cutPolylines = IntersectionUtils.GetIntersectionLines3D((IMultiPatch)selectedGeometry, cutSurfaces).ToList(); foreach (CutPolyline cutPoline in cutPolylines) { cutPoline.ObjectId = feature.OID; // Add the calculated lines for reference and traceability, filtering and subsequent cutting // with Success field equals null ProcessedCutLines?.Add(cutPoline); } if (cutPolylines.Count > 0) { var success = false; // TODO: Consider filter option for cut lines or even a clever path selection logic to avoid junctions: // Only use cut lines that are in the plane to connect disjoint cut line paths that are positive or negative // Consider adding an option to delete the negative or the positive side of the rings. var cutLineToUse = (IPolyline)GeometryUtils.Union( cutPolylines.Select(c => c.Polyline).ToList()); GeometryUtils.Simplify(cutLineToUse, true, false); if (!cutLineToUse.IsEmpty) { try { success = CutFeature(feature, cutLineToUse, ChangeAlongZSource.SourcePlane); } catch (Exception e) { _msg.WarnFormat("Cutting multipatch feature {0} failed.", GdbObjectUtils.ToString(feature)); _msg.Debug(e); } var appliedCutline = new CutPolyline(cutLineToUse); appliedCutline.SuccessfulCut = success; ProcessedCutLines?.Add(appliedCutline); } } }