Class ParallelWays


  • public class ParallelWays
    extends java.lang.Object
    Helper for ParallelWayAction.

    Computes a one-sided offset ("parallel") of a branchless path made of one or more ways.

    The algorithm is a proper one-sided buffer, which also works when the offset is much larger than the length of the segments of the source path (e.g. a maritime boundary 22 km off a coastline):

    1. Each segment is offset by the requested distance.
    2. At every vertex the two neighbouring offset segments are joined: on the inner side of a turn they are clipped at their intersection, on the outer side a mitre is used for gentle turns and a circular arc (approximated by chords) for sharp turns or when the mitre would overshoot too far.
    3. The resulting raw polyline is split at its self-intersections. Every piece is kept only if it lies at (at least) the offset distance from the source path. Pieces which are closer belong to inverted loops ("swallowtails") and are dropped. The remaining pieces are chained; the longest chain is the result.
    All calculations are done in projected coordinates.

    Contrary to earlier versions the nodes and ways are only created (and added to the data set) by commit(), since the number of nodes of the result depends on the offset. Use getOffsetPoints() to draw a preview while the offset is changed.

    • Field Summary

      Fields 
      Modifier and Type Field Description
      private double arcStep  
      private static int CAP
      marker for raw segments of the end caps, which are only used to trim the result and are never part of it
      private boolean closed  
      private boolean copyTags  
      static double DEFAULT_ARC_STEP_DEGREES
      Default angular step used to approximate circular arcs, in degrees
      private double[] dirX
      unit direction of segment i
      private double[] dirY  
      private double gridCell  
      private int[][] gridCells  
      private int gridCols  
      private double gridMinX  
      private double gridMinY  
      private int gridQuery  
      private int gridRows  
      private int[] gridStamp  
      private static int NO_NODE  
      private int nodeCount  
      private double[] px  
      private double[] py  
      private boolean resultClosed  
      private int[] resultPieceSeg  
      private int[] resultPointNode  
      private java.util.List<EastNorth> resultPts  
      private double[] segLen  
      private double[] segMaxX  
      private double[] segMaxY  
      private double[] segMinX
      bounding boxes of the source segments, used to speed up distance computations
      private double[] segMinY  
      private int[] segWay
      source way index for each source segment
      private java.util.List<Node> sortedNodes
      the source nodes, in path order (duplicates by coordinate removed, oriented like the reference way)
      private java.util.List<Way> sourceWays  
      private boolean[] wayForward
      whether the source way runs in the same direction as sortedNodes (aligned with sourceWays)
      private java.util.List<Way> ways  
    • Constructor Summary

      Constructors 
      Constructor Description
      ParallelWays​(java.util.Collection<Way> sourceWays, boolean copyTags, int refWayIndex)
      Constructs a new ParallelWays.
      ParallelWays​(java.util.Collection<Way> sourceWays, boolean copyTags, int refWayIndex, double arcStepDegrees)
      Constructs a new ParallelWays.
    • Method Summary

      All Methods Static Methods Instance Methods Concrete Methods 
      Modifier and Type Method Description
      private void addCap​(ParallelWays.RawPolyline raw, int k, double startAngle, int direction, double d, boolean leading)
      Adds the chords of a half circle around node k, starting at the given angle.
      private void addJoin​(ParallelWays.RawPolyline raw, int k, int prev, int next, double d)
      Adds the offset points at vertex k, where segment prev ends and segment next starts.
      private void buildGrid​(double r)
      Builds a uniform grid over the source segments, with a cell size of (at least) r, so that all segments within distance r of a point are found in the 3x3 cells around it.
      private ParallelWays.RawPolyline buildRawOffset​(double d)  
      private java.util.List<Way> buildWays()
      Builds the (not yet added) ways from the last computed offset path.
      void changeOffset​(double d)
      Offsets the way(s) d units.
      private void classify​(ParallelWays.RawPolyline raw, double r, ParallelWays.Points points, ParallelWays.Pieces pieces, int i, int sId, int eId)
      Determines the validity of the piece of raw segment i between two points, and adds the resulting piece(s).
      ParallelWays.ClosestPoint closestPoint​(EastNorth p)
      Finds the point of the source path closest to the given point.
      void commit()
      Creates the nodes and ways of the offset path (as computed by the last call to changeOffset(double)), and adds them to the edit data set by adding a new sequence command to the undo/redo queue.
      private double distanceToSource​(double x, double y, double cutoff)
      Distance from a point to the source path.
      private static ParallelWays.Chain findChainEndingAt​(java.util.List<ParallelWays.Chain> chains, int id, ParallelWays.Points points, double tolerance)
      Finds the chain ending at (or within the tolerance of) the given point, preferring an exact match.
      private void findSelfIntersections​(ParallelWays.RawPolyline raw, int rawSegCount, boolean[] skip, ParallelWays.Points points, java.util.List<java.util.List<double[]>> breaks)
      Finds all intersections between non adjacent segments of the raw polyline (sweep on x).
      private void forEachCell​(double minX, double minY, double maxX, double maxY, java.util.function.IntConsumer consumer)  
      java.util.List<EastNorth> getOffsetPoints()
      Returns the points of the offset path computed by the last call to changeOffset(double).
      java.util.List<Way> getWays()
      Returns the resulting parallel ways, available after commit().
      boolean isClosedPath()
      Determines if the nodes graph form a closed path
      private static boolean isForward​(java.util.List<Node> path, Way way, boolean cyclic)
      Checks whether a way runs in the same direction as a node list.
      boolean isResultClosed()
      Determines if the result of the last changeOffset(double) call is a closed ring.
      private boolean isValid​(double x, double y, double r, double tolerance, int arc)
      Checks whether a point of the raw polyline lies at (at least) distance r from the source path.
      private double locateTransition​(double[] s, double[] e, double a, double b, double r, double tolerance, int arc)
      Locates (by bisection) the parameter between a and b where the validity changes.
      private java.util.List<Command> makeAddWayAndNodesCommandList()  
      private Node makeNode​(int idx)  
      private static void replaceIntactArcsByMitres​(ParallelWays.RawPolyline raw, ParallelWays.Points points, java.util.List<java.lang.Integer> ids, java.util.List<java.lang.Integer> segs)
      Replaces arcs which are completely part of the result by a mitre (where the corner is gentle enough).
      private static boolean segmentIntersection​(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4, double[] uv)
      Segment/segment intersection.
      private void setEmptyResult()  
      private void trim​(ParallelWays.RawPolyline raw, double r)  
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Field Detail

      • CAP

        private static final int CAP
        marker for raw segments of the end caps, which are only used to trim the result and are never part of it
        See Also:
        Constant Field Values
      • copyTags

        private final boolean copyTags
      • arcStep

        private final double arcStep
      • sortedNodes

        private final java.util.List<Node> sortedNodes
        the source nodes, in path order (duplicates by coordinate removed, oriented like the reference way)
      • closed

        private final boolean closed
      • px

        private final double[] px
      • py

        private final double[] py
      • dirX

        private final double[] dirX
        unit direction of segment i
      • dirY

        private final double[] dirY
      • segLen

        private final double[] segLen
      • segMinX

        private final double[] segMinX
        bounding boxes of the source segments, used to speed up distance computations
      • segMinY

        private final double[] segMinY
      • segMaxX

        private final double[] segMaxX
      • segMaxY

        private final double[] segMaxY
      • wayForward

        private final boolean[] wayForward
        whether the source way runs in the same direction as sortedNodes (aligned with sourceWays)
      • segWay

        private final int[] segWay
        source way index for each source segment
      • ways

        private java.util.List<Way> ways
    • Constructor Detail

      • ParallelWays

        public ParallelWays​(java.util.Collection<Way> sourceWays,
                            boolean copyTags,
                            int refWayIndex)
        Constructs a new ParallelWays.
        Parameters:
        sourceWays - source ways
        copyTags - whether tags should be copied
        refWayIndex - Need a reference way to determine the direction of the offset when we manage multiple ways
        Throws:
        java.lang.IllegalArgumentException - if the ways do not form a branchless path
      • ParallelWays

        public ParallelWays​(java.util.Collection<Way> sourceWays,
                            boolean copyTags,
                            int refWayIndex,
                            double arcStepDegrees)
        Constructs a new ParallelWays.
        Parameters:
        sourceWays - source ways
        copyTags - whether tags should be copied
        refWayIndex - Need a reference way to determine the direction of the offset when we manage multiple ways
        arcStepDegrees - angular step (in degrees) of the chords approximating arcs at convex corners
        Throws:
        java.lang.IllegalArgumentException - if the ways do not form a branchless path
        Since:
        19624
    • Method Detail

      • isForward

        private static boolean isForward​(java.util.List<Node> path,
                                         Way way,
                                         boolean cyclic)
        Checks whether a way runs in the same direction as a node list.
        Parameters:
        path - the node list
        way - the way
        cyclic - whether the node list is a ring (without repeated closing node)
        Returns:
        true if the first segment of the way, found in the path, has the same orientation
      • isClosedPath

        public boolean isClosedPath()
        Determines if the nodes graph form a closed path
        Returns:
        true if the nodes graph form a closed path
      • closestPoint

        public ParallelWays.ClosestPoint closestPoint​(EastNorth p)
        Finds the point of the source path closest to the given point. Used to relate the offset to the part of the path the mouse is currently next to (rather than to the segment where the drag started).
        Parameters:
        p - the point (projected coordinates)
        Returns:
        the closest point, the segment it lies on and the signed distance
        Since:
        19624
      • changeOffset

        public void changeOffset​(double d)
        Offsets the way(s) d units. Positive d means to the left (relative to the reference way)
        Parameters:
        d - offset
      • buildGrid

        private void buildGrid​(double r)
        Builds a uniform grid over the source segments, with a cell size of (at least) r, so that all segments within distance r of a point are found in the 3x3 cells around it.
        Parameters:
        r - the (absolute) offset
      • forEachCell

        private void forEachCell​(double minX,
                                 double minY,
                                 double maxX,
                                 double maxY,
                                 java.util.function.IntConsumer consumer)
      • addCap

        private void addCap​(ParallelWays.RawPolyline raw,
                            int k,
                            double startAngle,
                            int direction,
                            double d,
                            boolean leading)
        Adds the chords of a half circle around node k, starting at the given angle. The chords are marked as CAP: they are only used to trim other pieces.
        Parameters:
        raw - the raw polyline
        k - source node index
        startAngle - angle of the first point of the half circle
        direction - rotation direction (+1 counter clockwise)
        d - the offset
        leading - true if the cap precedes the offset path (the end point of the half circle is then added by the caller), false if it follows it (the start point has been added by the caller)
      • addJoin

        private void addJoin​(ParallelWays.RawPolyline raw,
                             int k,
                             int prev,
                             int next,
                             double d)
        Adds the offset points at vertex k, where segment prev ends and segment next starts.
        Parameters:
        raw - the raw polyline
        k - source node index
        prev - index of the segment ending at k
        next - index of the segment starting at k
        d - the offset
      • classify

        private void classify​(ParallelWays.RawPolyline raw,
                              double r,
                              ParallelWays.Points points,
                              ParallelWays.Pieces pieces,
                              int i,
                              int sId,
                              int eId)
        Determines the validity of the piece of raw segment i between two points, and adds the resulting piece(s). The validity is sampled at both ends and in the middle; if it changes, the transition point is located and the piece is split there.
      • locateTransition

        private double locateTransition​(double[] s,
                                        double[] e,
                                        double a,
                                        double b,
                                        double r,
                                        double tolerance,
                                        int arc)
        Locates (by bisection) the parameter between a and b where the validity changes.
        Returns:
        the parameter of the transition
      • isValid

        private boolean isValid​(double x,
                                double y,
                                double r,
                                double tolerance,
                                int arc)
        Checks whether a point of the raw polyline lies at (at least) distance r from the source path.
        Parameters:
        x - point
        y - point
        r - offset
        tolerance - allowed deficit
        arc - if the point lies on an arc chord: the center node of the arc; the point is then projected onto the arc before testing, else NO_NODE
        Returns:
        true if the point is valid
      • findSelfIntersections

        private void findSelfIntersections​(ParallelWays.RawPolyline raw,
                                           int rawSegCount,
                                           boolean[] skip,
                                           ParallelWays.Points points,
                                           java.util.List<java.util.List<double[]>> breaks)
        Finds all intersections between non adjacent segments of the raw polyline (sweep on x).
        Parameters:
        raw - the raw polyline
        rawSegCount - number of raw segments
        skip - raw segments to ignore
        points - point table, intersection points are appended
        breaks - per raw segment list of (param, pointId), filled
      • segmentIntersection

        private static boolean segmentIntersection​(double x1,
                                                   double y1,
                                                   double x2,
                                                   double y2,
                                                   double x3,
                                                   double y3,
                                                   double x4,
                                                   double y4,
                                                   double[] uv)
        Segment/segment intersection.
        Parameters:
        uv - output: parameters along the first and the second segment
        Returns:
        true if the segments intersect (touching end points count as intersection)
      • distanceToSource

        private double distanceToSource​(double x,
                                        double y,
                                        double cutoff)
        Distance from a point to the source path.
        Parameters:
        x - point
        y - point
        cutoff - the search can stop as soon as a distance below this value is found
        Returns:
        the distance (or any value below cutoff if such a distance exists)
      • getOffsetPoints

        public java.util.List<EastNorthgetOffsetPoints()
        Returns the points of the offset path computed by the last call to changeOffset(double). For a closed result the first point is not repeated at the end, see isResultClosed().
        Returns:
        the offset points (projected coordinates), empty if nothing has been computed or nothing remains
        Since:
        19624
      • isResultClosed

        public boolean isResultClosed()
        Determines if the result of the last changeOffset(double) call is a closed ring.
        Returns:
        true if the offset path is a closed ring
        Since:
        19624
      • commit

        public void commit()
        Creates the nodes and ways of the offset path (as computed by the last call to changeOffset(double)), and adds them to the edit data set by adding a new sequence command to the undo/redo queue.

        Does nothing if there is no offset path.

      • buildWays

        private java.util.List<WaybuildWays()
        Builds the (not yet added) ways from the last computed offset path.
        Returns:
        the ways, in the order of the source ways; ways swallowed by the offset are omitted
      • getWays

        public final java.util.List<WaygetWays()
        Returns the resulting parallel ways, available after commit().
        Returns:
        the resulting parallel ways (empty before commit)