Ignore:
Timestamp:
2014-03-02T12:06:31+01:00 (6 years ago)
Author:
bastiK
Message:

applied #6819 - Align nodes in line should respect straight crossing lines (patch by Balaitous)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/AlignInLineAction.java

    r6380 r6893  
    99import java.util.ArrayList;
    1010import java.util.Collection;
     11import java.util.HashMap;
     12import java.util.HashSet;
    1113import java.util.List;
    1214
     
    6971
    7072    private void showWarning() {
    71         new Notification(
    72                 tr("Please select at least three nodes."))
    73                 .setIcon(JOptionPane.INFORMATION_MESSAGE)
    74                 .show();
     73        showWarning(tr("Please select at least three nodes."));
     74    }
     75
     76    private void showWarning(String msg) {
     77        new Notification(msg)
     78            .setIcon(JOptionPane.INFORMATION_MESSAGE)
     79            .show();
    7580    }
    7681
     
    114119        //// Decide what to align based on selection:
    115120
    116         /// Only ways selected -> Align their nodes.
    117         if ((selectedNodes.isEmpty()) && (selectedWays.size() == 1)) { // TODO: handle multiple ways
    118             for (Way way : selectedWays) {
    119                 nodes.addAll(way.getNodes());
    120             }
    121             // use the nodes furthest apart as anchors
    122             nodePairFurthestApart(nodes, anchors);
     121        /// Only ways selected -> For each way align their nodes taking care of intersection
     122        if(selectedNodes.isEmpty() && !selectedWays.isEmpty()) {
     123            alignMultiWay(selectedWays);
     124            return;
    123125        }
    124126        /// More than 3 nodes selected -> align those nodes
     
    221223    }
    222224
     225    /**
     226     * Align way in case of multiple way #6819
     227     * @param ways Collection of way to align
     228     */
     229    private void alignMultiWay(Collection<Way> ways) {
     230        // Collect all nodes and compute line equation
     231        HashSet<Node> nodes = new HashSet<Node>();
     232        HashMap<Way, Line> lines = new HashMap<Way, Line>();
     233        for(Way w: ways) {
     234            if(w.firstNode() == w.lastNode()) {
     235                showWarning(tr("Can not align a polygon. Abort."));
     236                return;
     237            }
     238            nodes.addAll(w.getNodes());
     239            lines.put(w, new Line(w));
     240        }
     241        Collection<Command> cmds = new ArrayList<Command>(nodes.size());
     242        List<Way> referers = new ArrayList<Way>(ways.size());
     243        for(Node n: nodes) {
     244            referers.clear();
     245            for(OsmPrimitive o: n.getReferrers())
     246                if(ways.contains(o))
     247                    referers.add((Way) o);
     248            if(referers.size() == 1) {
     249                Way way = referers.get(0);
     250                if(n == way.firstNode() || n == way.lastNode()) continue;
     251                cmds.add(lines.get(way).projectionCommand(n));
     252            }
     253            else if(referers.size() == 2) {
     254                Command cmd = lines.get(referers.get(0)).intersectionCommand(n, lines.get(referers.get(1)));
     255                if(cmd == null) {
     256                    showWarning(tr("Two parallels ways found. Abort."));
     257                    return;
     258                }
     259                cmds.add(cmd);
     260            }
     261            else {
     262                showWarning(tr("Intersection of three or more ways can not be solved. Abort."));
     263                return;
     264            }
     265        }
     266        Main.main.undoRedo.add(new SequenceCommand(tr("Align Nodes in Line"), cmds));
     267        Main.map.repaint();
     268    }
     269
     270    /**
     271     * Class that describe a line
     272     */
     273    private class Line {
     274
     275        /**
     276         * Line equation ax + by + c = 0
     277         * Such as a^2 + b^2 = 1, ie (-b, a) is a unit vector of line
     278         */
     279        private double a, b, c; // Line equation ax+by+c=0
     280        /**
     281         * (xM, yM) are coordinate of a point of the line
     282         */
     283        private double xM, yM; // Coordinate of a point of the line
     284
     285        /**
     286         * Init a line equation from a way.
     287         * @param way
     288         */
     289        public Line(Way way) {
     290            xM = way.firstNode().getEastNorth().getX();
     291            yM = way.firstNode().getEastNorth().getY();
     292            double xB = way.lastNode().getEastNorth().getX();
     293            double yB = way.lastNode().getEastNorth().getY();
     294            a = yB - yM;
     295            b = xM - xB;
     296            double norm = Math.sqrt(a*a + b*b);
     297            if (norm == 0) {
     298                norm = 1;
     299            }
     300            a /= norm;
     301            b /= norm;
     302            c = -(a*xM + b*yM);
     303        }
     304
     305        /**
     306         * Orthogonal projection of a node N along this line.
     307         * @param n Node to be projected
     308         * @return The command that do the projection of this node
     309         */
     310        public Command projectionCommand(Node n) {
     311            double s = (xM - n.getEastNorth().getX()) * a + (yM - n.getEastNorth().getY()) * b;
     312            return new MoveCommand(n, a*s, b*s);
     313        }
     314
     315        /**
     316         * Intersection of two line.
     317         * @param n Node to move to the intersection
     318         * @param other Second line for intersection
     319         * @return The command that move the node or null if line are parallels
     320         */
     321        public Command intersectionCommand(Node n, Line other) {
     322            double d = this.a * other.b - other.a * this.b;
     323            if(d == 0) return null;
     324            double x = (this.b * other.c - other.b * this.c) / d;
     325            double y = (other.a * this.c - this.a * other.c) / d;
     326            return new MoveCommand(n, x - n.getEastNorth().getX(), y - n.getEastNorth().getY());
     327        }
     328    }
     329
    223330    @Override
    224331    protected void updateEnabledState() {
Note: See TracChangeset for help on using the changeset viewer.