Beispiel #1
0
        /// <summary>
        /// Unions a valid polygonal coverage or linear network.
        /// </summary>
        /// <param name="coverage">A coverage of polygons or lines</param>
        /// <returns>The union of the coverage</returns>
        /// <exception cref="TopologyException">Thrown in some cases if the coverage is invalid</exception>
        public static Geometry Union(Geometry coverage)
        {
            var noder = new SegmentExtractingNoder();

            // a precision model is not needed since no noding is done
            return(OverlayNG.Union(coverage, null, noder));
        }
        /// <summary>
        /// Reduces the precision of a geometry by rounding and snapping it to the
        /// supplied <see cref="PrecisionModel"/>.<br/>
        /// The input geometry must be polygonal or linear.
        /// <para/>
        /// The output is always a valid geometry.  This implies that input components
        /// may be merged if they are closer than the grid precision.
        /// if merging is not desired, then the individual geometry components
        /// should be processed separately.
        /// <para/>
        /// The output is fully noded (i.e. coincident lines are merged and noded).
        /// This provides an effective way to node / snap-round a collection of <see cref="LineString"/>s.
        /// </summary>
        /// <param name="geom">The geometry to reduce</param>
        /// <param name="pm">The precision model to use</param>
        /// <returns>The precision-reduced geometry</returns>
        public static Geometry ReducePrecision(Geometry geom, PrecisionModel pm)
        {
            if (geom == null)
            {
                throw new ArgumentNullException(nameof(geom));
            }

            var ov = new OverlayNG(geom, null, pm, SpatialFunction.Union);

            /*
             * Ensure reducing a area only produces polygonal result.
             * (I.e. collapse lines are not output)
             */
            if (geom.Dimension == Dimension.Surface)
            {
                ov.AreaResultOnly = true;
            }
            try
            {
                var reduced = ov.GetResult();
                return(reduced);
            }
            catch (TopologyException ex)
            {
                throw new ArgumentException("Reduction failed, possible invalid input", ex);
            }
        }
Beispiel #3
0
        /// <summary>
        /// Computes a union operation on
        /// the given geometry, with the supplied precision model.
        /// <para/>
        /// The input must be a valid geometry.
        /// Collections must be homogeneous.
        /// <para/>
        /// To union an overlapping set of polygons in a more performant way use <see cref="UnaryUnionNG"/>.
        /// To union a polygonal coverage or linear network in a more performant way,
        /// use <see cref="CoverageUnion"/>.
        /// </summary>
        /// <param name="geom">The geometry</param>
        /// <param name="pm">The precision model to use</param>
        /// <returns>The result of the union operation</returns>
        /// <seealso cref="OverlayMixedPoints"/>
        internal static Geometry Union(Geometry geom, PrecisionModel pm)
        {
            var ov     = new OverlayNG(geom, null, pm, SpatialFunction.Union);
            var geomOv = ov.GetResult();

            return(geomOv);
        }
Beispiel #4
0
        /// <summary>
        /// Computes an overlay operation for
        /// the given geometry operands, with the
        /// noding strategy determined by the precision model.
        /// </summary>
        /// <param name="geom0">The first geometry argument</param>
        /// <param name="geom1">The second geometry argument</param>
        /// <param name="opCode">The code for the desired overlay operation</param>
        /// <param name="pm">The precision model to use</param>
        /// <returns>The result of the overlay operation</returns>
        public static Geometry Overlay(Geometry geom0, Geometry geom1,
                                       SpatialFunction opCode, PrecisionModel pm)
        {
            var ov     = new OverlayNG(geom0, geom1, pm, opCode);
            var geomOv = ov.GetResult();

            return(geomOv);
        }
Beispiel #5
0
        /// <summary>
        /// Computes a union of a single geometry using a custom noder.
        /// <para/>
        /// The primary use of this is to support coverage union.
        /// Because of this the overlay is performed using strict mode.
        /// </summary>
        /// <param name="geom">The geometry to union</param>
        /// <param name="pm">The precision model to use (maybe be <c>null</c>)</param>
        /// <param name="noder">The noder to use</param>
        /// <returns>the result geometry</returns>
        /// <seealso cref="CoverageUnion"/>
        internal static Geometry Union(Geometry geom, PrecisionModel pm, INoder noder)
        {
            var ov = new OverlayNG(geom, null, pm, SpatialFunction.Union);

            ov.Noder      = noder;
            ov.StrictMode = true;
            var geomOv = ov.GetResult();

            return(geomOv);
        }
Beispiel #6
0
        /// <summary>
        /// Computes an overlay operation on the given geometry operands,
        /// using a supplied <see cref="INoder"/>.
        /// </summary>
        /// <param name="geom0">The first geometry argument</param>
        /// <param name="geom1">The second geometry argument</param>
        /// <param name="opCode">The code for the desired overlay operation</param>
        /// <param name="noder">The noder to use</param>
        /// <returns>The result of the overlay operation</returns>
        public static Geometry Overlay(Geometry geom0, Geometry geom1,
                                       SpatialFunction opCode, INoder noder)
        {
            var ov = new OverlayNG(geom0, geom1, null, opCode);

            ov.Noder = noder;
            var geomOv = ov.GetResult();

            return(geomOv);
        }
Beispiel #7
0
        private Geometry PrepareNonPoint(Geometry geomInput)
        {
            // if non-point not in output no need to node it
            if (_resultDim == 0)
            {
                return(geomInput);
            }

            // Node and round the non-point geometry for output
            var geomPrep = OverlayNG.Union(_geomNonPointInput, _pm);

            return(geomPrep);
        }
        /// <summary>
        /// Marks an edge which forms part of the boundary of the result area.
        /// This is determined by the overlay operation being executed,
        /// and the location of the edge.
        /// The relevant location is either the right side of a boundary edge,
        /// or the line location of a non-boundary edge.
        /// </summary>
        /// <param name="e">The edge to mark</param>
        /// <param name="overlayOpCode">The overlay operation</param>
        public void MarkInResultArea(OverlayEdge e, SpatialFunction overlayOpCode)
        {
            var label = e.Label;

            if (label.IsBoundaryEither &&
                OverlayNG.IsResultOfOp(
                    overlayOpCode,
                    label.GetLocationBoundaryOrLine(0, Position.Right, e.IsForward),
                    label.GetLocationBoundaryOrLine(1, Position.Right, e.IsForward)))
            {
                e.MarkInResultArea();
            }
            //Debug.println("markInResultArea: " + e);
        }
        /// <summary>
        /// Self-snaps a geometry by running a union operation with it as the only input.
        /// This helps to remove narrow spike/gore artifacts to simplify the geometry,
        /// which improves robustness.
        /// Collapsed artifacts are removed from the result to allow using
        /// it in further overlay operations.
        /// </summary>
        /// <param name="geom">Geometry to self-snap</param>
        /// <param name="snapTol">Snap tolerance</param>
        /// <returns>The snapped geometry (homogenous)</returns>
        private static Geometry SnapSelf(Geometry geom, double snapTol)
        {
            var ov        = new OverlayNG(geom, null);
            var snapNoder = new SnappingNoder(snapTol);

            ov.Noder = snapNoder;

            /*
             * Ensure the result is not mixed-dimension,
             * since it will be used in further overlay computation.
             * It may however be lower dimension, if it collapses completely due to snapping.
             */
            ov.StrictMode = true;
            return(ov.GetResult());
        }
Beispiel #10
0
        /// <summary>
        /// Unions a collection of geometries
        /// using a given precision model.
        /// <para/>
        /// This class is most useful for performing UnaryUnion using
        /// a fixed-precision model.
        /// For unary union using floating precision,
        /// <see cref="OverlayNGRobust.Union(Geometry)"/> should be used.
        /// </summary>
        /// <param name="geom">The geometry to union</param>
        /// <param name="pm">The precision model to use</param>
        /// <returns>The union of the geometries</returns>
        /// <seealso cref="OverlayNGRobust"/>
        public static Geometry Union(Geometry geom, PrecisionModel pm)
        {
            if (geom == null)
            {
                throw new ArgumentNullException(nameof(geom));
            }

            var unionSRFun = new UnionStrategy((g0, g1) =>
                                               OverlayNG.Overlay(g0, g1, SpatialFunction.Union, pm), OverlayUtility.IsFloating(pm));

            var op = new UnaryUnionOp(geom)
            {
                UnionStrategy = unionSRFun
            };

            return(op.Union());
        }
        //===============================================

        /// <summary>
        /// Attempt Overlay using Snap-Rounding with an automatically-determined
        /// scale factor.
        /// </summary>
        /// <param name="geom0"></param>
        /// <param name="geom1"></param>
        /// <param name="opCode"></param>
        /// <returns>the computed overlay result, or null if the overlay fails</returns>
        public static Geometry OverlaySR(Geometry geom0, Geometry geom1, SpatialFunction opCode)
        {
            Geometry result;

            try
            {
                //System.out.println("OverlaySnapIfNeeded: trying snap-rounding");
                double scaleSafe = PrecisionUtility.SafeScale(geom0, geom1);
                var    pmSafe    = new PrecisionModel(scaleSafe);
                result = OverlayNG.Overlay(geom0, geom1, opCode, pmSafe);
                return(result);
            }
            catch (TopologyException ex)
            {
                //---- ignore exception, return null result to indicate failure
            }
            return(null);
        }
Beispiel #12
0
        /// <summary>
        /// Computes an overlay operation on
        /// the given geometry operands,
        /// using the precision model of the geometry.
        /// and an appropriate noder.
        /// <para/>
        /// The noder is chosen according to the precision model specified.
        /// <list type="bullet">
        /// <item><description>For <see cref="PrecisionModels.Fixed"/>
        /// a snap-rounding noder is used, and the computation is robust.</description></item>
        /// <item><description>For <see cref="PrecisionModels.Floating"/>
        /// a non-snapping noder is used,
        /// and this computation may not be robust.
        /// If errors occur a <see cref="TopologyException"/> is thrown.</description></item>
        /// </list>
        /// </summary>
        /// <param name="geom0">The first geometry argument</param>
        /// <param name="geom1">The second geometry argument</param>
        /// <param name="opCode">The code for the desired overlay operation</param>
        /// <returns>The result of the overlay operation</returns>
        public static Geometry Overlay(Geometry geom0, Geometry geom1, SpatialFunction opCode)
        {
            var ov = new OverlayNG(geom0, geom1, opCode);

            return(ov.GetResult());
        }
        /// <summary>
        /// Overlay two geometries, using heuristics to ensure
        /// computation completes correctly.
        /// In practice the heuristics are observed to be fully correct.
        /// </summary>
        /// <param name="geom0">A geometry</param>
        /// <param name="geom1">A geometry</param>
        /// <param name="opCode">The overlay operation code</param>
        /// <returns>The overlay result geometry</returns>
        public static Geometry Overlay(Geometry geom0, Geometry geom1, SpatialFunction opCode)
        {
            if (geom0 == null)
            {
                throw new ArgumentNullException(nameof(geom0));
            }

            switch (opCode)
            {
            case SpatialFunction.Intersection:
            case SpatialFunction.Union:
            case SpatialFunction.Difference:
            case SpatialFunction.SymDifference:
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(opCode), opCode, "Only Intersection, Union, Difference, and SymDifference are recognized at this time.");
            }

            /*
             * First try overlay with a PrecisionModels.Floating noder, which is
             * fast and causes least change to geometry coordinates
             * By default the noder is validated, which is required in order
             * to detect certain invalid noding situations which otherwise
             * cause incorrect overlay output.
             */
            try
            {
                return(OverlayNG.Overlay(geom0, geom1, opCode));
            }
            catch (Exception ex)
            {
                /*
                 * On failure retry using snapping noding with a "safe" tolerance.
                 * if this throws an exception just let it go,
                 * since it is something that is not a TopologyException
                 */
                try
                {
                    if (OverlaySnapTries(geom0, geom1, opCode) is Geometry result)
                    {
                        return(result);
                    }
                }
                catch (Exception ex2)
                {
                    throw new AggregateException(ex, ex2);
                }

                /*
                 * On failure retry using snap-rounding with a heuristic scale factor (grid size).
                 */
                try
                {
                    if (OverlaySR(geom0, geom1, opCode) is Geometry result)
                    {
                        return(result);
                    }
                }
                catch (Exception ex2)
                {
                    throw new AggregateException(ex, ex2);
                }

                /*
                 * Just can't get overlay to work, so throw original error.
                 */
                throw;
            }
        }
        private static Geometry OverlaySnapTol(Geometry geom0, Geometry geom1, SpatialFunction opCode, double snapTol)
        {
            var snapNoder = new SnappingNoder(snapTol);

            return(OverlayNG.Overlay(geom0, geom1, opCode, snapNoder));
        }
Beispiel #15
0
        /// <summary>
        /// Checks if the topology indicated by an edge label
        /// determines that this edge should be part of a result line.
        /// <para/>
        /// Note that the logic here relies on the semantic
        /// that for intersection lines are only returned if
        /// there is no result area components.
        /// </summary>
        /// <param name="lbl">The label for an edge</param>
        /// <returns><c>true</c> if the edge should be included in the result</returns>
        private bool IsResultLine(OverlayLabel lbl)
        {
            /*
             * Omit edge which is a boundary of a single geometry
             * (i.e. not a collapse or line edge as well).
             * These are only included if part of a result area.
             * This is a short-circuit for the most common area edge case
             */
            if (lbl.IsBoundarySingleton)
            {
                return(false);
            }

            /*
             * Omit edge which is a collapse along a boundary.
             * I.e a result line edge must be from a input line
             * OR two coincident area boundaries.
             *
             * This logic is only used if not including collapse lines in result.
             */
            if (!_isAllowCollapseLines &&
                lbl.IsBoundaryCollapse)
            {
                return(false);
            }

            /*
             * Omit edge which is a collapse interior to its parent area.
             * (E.g. a narrow gore, or spike off a hole)
             */
            if (lbl.IsInteriorCollapse)
            {
                return(false);
            }

            /*
             * For ops other than Intersection, omit a line edge
             * if it is interior to the other area.
             *
             * For Intersection, a line edge interior to an area is included.
             */
            if (_opCode != OverlayNG.INTERSECTION)
            {
                /*
                 * Omit collapsed edge in other area interior.
                 */
                if (lbl.IsCollapseAndNotPartInterior)
                {
                    return(false);
                }

                /*
                 * If there is a result area, omit line edge inside it.
                 * It is sufficient to check against the input area rather
                 * than the result area,
                 * because if line edges are present then there is only one input area,
                 * and the result area must be the same as the input area.
                 */
                if (_hasResultArea && lbl.IsLineInArea(_inputAreaIndex))
                {
                    return(false);
                }
            }

            /*
             * Include line edge formed by touching area boundaries,
             * if enabled.
             */
            if (_isAllowMixedResult &&
                _opCode == OverlayNG.INTERSECTION && lbl.IsBoundaryTouch)
            {
                return(true);
            }

            /*
             * Finally, determine included line edge
             * according to overlay op boolean logic.
             */
            var aLoc = EffectiveLocation(lbl, 0);
            var bLoc = EffectiveLocation(lbl, 1);

            bool isInResult = OverlayNG.IsResultOfOp(_opCode, aLoc, bLoc);

            return(isInResult);
        }