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