Example #1
0
        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);
        }
Example #2
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);
        }
Example #3
0
        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);
                }
            }
        }