Ticket #67: make-parallel-way.patch

File make-parallel-way.patch, 14.5 KB (added by olejorgenb, 10 years ago)

Rough initial patch

  • new file src/org/openstreetmap/josm/actions/MakeParallelWay.java

    diff --git a/images/mapmode/parallel.png b/images/mapmode/parallel.png
    new file mode 100644
    index 0000000..a02c6fc
    Binary files /dev/null and b/images/mapmode/parallel.png differ
    diff --git a/src/org/openstreetmap/josm/actions/MakeParallelWay.java b/src/org/openstreetmap/josm/actions/MakeParallelWay.java
    new file mode 100644
    index 0000000..739779f
    - +  
     1// License: GPL. Copyright 2007 by Immanuel Scholz and others
     2// Author: David Earl
     3package org.openstreetmap.josm.actions;
     4
     5import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
     6import static org.openstreetmap.josm.tools.I18n.tr;
     7
     8import java.awt.Cursor;
     9import java.awt.Point;
     10import java.awt.event.KeyEvent;
     11import java.awt.event.MouseEvent;
     12import java.util.ArrayList;
     13import java.util.Collection;
     14import java.util.List;
     15
     16import org.openstreetmap.josm.Main;
     17import org.openstreetmap.josm.actions.mapmode.MapMode;
     18import org.openstreetmap.josm.command.AddCommand;
     19import org.openstreetmap.josm.command.Command;
     20import org.openstreetmap.josm.command.SequenceCommand;
     21import org.openstreetmap.josm.data.coor.EastNorth;
     22import org.openstreetmap.josm.data.osm.DataSet;
     23import org.openstreetmap.josm.data.osm.Node;
     24import org.openstreetmap.josm.data.osm.Way;
     25import org.openstreetmap.josm.data.osm.WaySegment;
     26import org.openstreetmap.josm.gui.MapFrame;
     27import org.openstreetmap.josm.tools.Geometry;
     28import org.openstreetmap.josm.tools.Shortcut;
     29
     30public final class MakeParallelWay extends MapMode {
     31
     32    public MakeParallelWay(MapFrame mapFrame) {
     33        super(tr("Parallel"), "parallel",
     34                tr("Makes a paralell copy of the selected way(s)"),
     35                Shortcut.registerShortcut("mapmode:parallel", tr("Mode: {0}", tr("Parallel")),
     36                        KeyEvent.VK_P, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT),
     37                        mapFrame, Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
     38        putValue("help", ht("/Action/Parallel"));
     39    }
     40
     41    @Override public String getModeHelpText() {
     42        return "Drag a way to make a parallel copy";
     43    }
     44    @Override public void enterMode() {
     45        super.enterMode();
     46        Main.map.mapView.addMouseListener(this);
     47        Main.map.mapView.addMouseMotionListener(this);
     48    }
     49
     50    @Override public void exitMode() {
     51        Main.map.mapView.removeMouseListener(this);
     52        Main.map.mapView.removeMouseMotionListener(this);
     53        Main.map.statusLine.setDist(-1);
     54        Main.map.statusLine.repaint();
     55        super.exitMode();
     56    }
     57    @Override public void mousePressed(MouseEvent e) {
     58        if(!Main.map.mapView.isActiveLayerVisible())
     59            return;
     60        if (!(Boolean)this.getValue("active"))
     61            return;
     62        if (e.getButton() != MouseEvent.BUTTON1)
     63            return;
     64        Point p = e.getPoint();
     65        referenceSegment = Main.map.mapView.getNearestWaySegment(p, Way.isUsablePredicate, true);
     66        if(referenceSegment == null)
     67            return;
     68        Collection<Way> selectedWays = Main.main.getCurrentDataSet().getSelectedWays();
     69        if(Main.main.getCurrentDataSet().selectionEmpty()) {
     70
     71        }
     72
     73        pways = new ParallelWays(referenceSegment.way);
     74        getCurrentDataSet().setSelected(pways.refWay);
     75    }
     76    @Override public void mouseReleased(MouseEvent e) {
     77        // if only a click we should dispose of the created way
     78        if (pways != null) {
     79            //            pways.commit(Main.main.getCurrentDataSet());
     80        }
     81        pways = null;
     82    }
     83    @Override public void mouseDragged(MouseEvent e) {
     84        if(pways == null)
     85            return;
     86        Point p = e.getPoint();
     87        EastNorth enp = Main.map.mapView.getEastNorth((int)p.getX(), (int)p.getY());
     88        EastNorth enOnRefLine = Geometry.closestPointToLine(referenceSegment.getFirstNode().getEastNorth(),
     89                referenceSegment.getSecondNode().getEastNorth(), enp);
     90        double d = enp.distance(enOnRefLine);
     91        boolean toTheRight = Geometry.isToTheRightSideOfLine(
     92                referenceSegment.getFirstNode(), referenceSegment.getFirstNode(), referenceSegment.getSecondNode(), new Node(enp));
     93        if (toTheRight) {
     94            d = -d;
     95        }
     96        pways.changeOffset(d);
     97        Main.map.statusLine.setDist(Math.abs(d));
     98        Main.map.statusLine.repaint();
     99        Main.map.mapView.repaint();
     100    }
     101    private WaySegment referenceSegment;
     102    private ParallelWays pways;
     103
     104    static class ParallelWays {
     105        //        public ArrayList<Way> ways;
     106        public Way refWay;
     107        public Way pWay;
     108
     109        int nodeCount;
     110        EastNorth[] pts;
     111        EastNorth[] dts;
     112
     113        public ParallelWays(Way way) {
     114            this.refWay = way;
     115            nodeCount = this.refWay.getNodesCount();
     116            pts = new EastNorth[nodeCount];
     117            dts = new EastNorth[nodeCount-1];
     118
     119            int i = 0;
     120            for(Node n : way.getNodes()) {
     121                EastNorth t = n.getEastNorth();
     122                pts[i] = t;
     123                i++;
     124            }
     125            for(i = 0; i < nodeCount-1; i++) {
     126                double dx = pts[i+1].getX() - pts[i].getX();
     127                double dy = pts[i+1].getY() - pts[i].getY();
     128                double len = Math.sqrt(dx*dx + dy*dy);
     129                dts[i] = new EastNorth(-dy / len, dx / len);
     130            }
     131
     132            pWay = new Way();
     133            for(i = 0; i < nodeCount-1; i++) {
     134                Node n = new Node(pts[i]);
     135                pWay.addNode(n);
     136            }
     137            if (way.isClosed()) {
     138                pWay.addNode(pWay.getNode(0));
     139            } else {
     140                Node n = new Node(pts[nodeCount-1]);
     141                pWay.addNode(n);
     142            }
     143            commit(getCurrentDataSet());
     144        }
     145
     146        public void changeOffset(double d) {
     147            EastNorth[] ppts = new EastNorth[nodeCount];
     148
     149            EastNorth prevA = add(pts[0], mul(dts[0], d));
     150            EastNorth prevB = add(pts[1], mul(dts[0], d));
     151            for(int i = 1; i < nodeCount-1; i++) {
     152                EastNorth A = add(pts[i], mul(dts[i], d));
     153                EastNorth B = add(pts[i+1], mul(dts[i], d));
     154                if (Geometry.segmentsParallel(A, B, prevA, prevB)) {
     155                    ppts[i] = A;
     156                } else {
     157                    ppts[i] = Geometry.getLineLineIntersection(A, B, prevA, prevB);
     158                }
     159                prevA = A;
     160                prevB = B;
     161            }
     162            if(refWay.isClosed()) {
     163                EastNorth A = add(pts[0], mul(dts[0], d));
     164                EastNorth B = add(pts[1], mul(dts[0], d));
     165                if (Geometry.segmentsParallel(A, B, prevA, prevB)) {
     166                    ppts[0] = A;
     167                } else {
     168                    ppts[0] = Geometry.getLineLineIntersection(A, B, prevA, prevB);
     169                }
     170                ppts[nodeCount-1] = ppts[0];
     171            } else {
     172                ppts[0] = add(pts[0], mul(dts[0], d));
     173                ppts[nodeCount-1] = add(pts[nodeCount-1], mul(dts[nodeCount-2], d));
     174            }
     175
     176            for(int i = 0; i < nodeCount; i++) {
     177                pWay.getNode(i).setEastNorth(ppts[i]);
     178            }
     179        }
     180        public static List<Command> makeAddWayAndNodesCommandList(Way w) {
     181            ArrayList<Command> commands = new ArrayList<Command>(w.getNodesCount()+1);
     182            for(int i = 0; i < w.getNodesCount()-1; i++) {
     183                commands.add(new AddCommand(w.getNode(i)));
     184            }
     185            if(!w.isClosed()){
     186                commands.add(new AddCommand(w.getNode(w.getNodesCount()-1)));
     187            }
     188            commands.add(new AddCommand(w));
     189            return commands;
     190        }
     191        public void commit(DataSet ds) {
     192            SequenceCommand undoCommand = new SequenceCommand("Make parallel way", makeAddWayAndNodesCommandList(pWay));
     193            Main.main.undoRedo.add(undoCommand);
     194        }
     195    }
     196
     197    //    @Override
     198    //    public void actionPerformed(ActionEvent e) {
     199    //        return;
     200    //        //        double d = Double.parseDouble(JOptionPane.showInputDialog("Input offset"));
     201    //        //        Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected();
     202    //        //        Collection<Way> selectedWays = new ArrayList<Way>();
     203    //        //        for(OsmPrimitive prim : selection) {
     204    //        //            if (prim instanceof Way) {
     205    //        //                selectedWays.add((Way)prim);
     206    //        //            }
     207    //        //        }
     208    //        //        for(Way w : makeParallelWays(selectedWays, d)) {
     209    //        //            Main.main.getCurrentDataSet().addPrimitive(w);
     210    //        //        }
     211    //    }
     212
     213    //    private Collection<Way> makeParallelWays(Collection<Way> Ways, double d) {
     214    //        Collection<Way> parallelWays = new ArrayList<Way>();
     215    //        for (Way way : Ways) {
     216    //            parallelWays.add(makeParallelWay(way, d));
     217    //        }
     218    //        return parallelWays;
     219    //    }
     220
     221    //    private Way makeParallelWay(Way way, double d) {
     222    //        int nodeCount = way.getNodesCount();
     223    //        EastNorth[] pts = new EastNorth[nodeCount];
     224    //        EastNorth[] dts = new EastNorth[nodeCount-1];
     225    //
     226    //        int i = 0;
     227    //        for(Node n : way.getNodes()) {
     228    //            EastNorth t = n.getEastNorth();
     229    //            pts[i] = t;
     230    //            i++;
     231    //        }
     232    //        for(i = 0; i < nodeCount-1; i++) {
     233    //            double dx = pts[i+1].getX() - pts[i].getX();
     234    //            double dy = pts[i+1].getY() - pts[i].getY();
     235    //            double len = Math.sqrt(dx*dx + dy*dy);
     236    //            dts[i] = new EastNorth(-dy / len, dx / len);
     237    //        }
     238    //        EastNorth[] ppts = new EastNorth[nodeCount];
     239    //        EastNorth prevA = add(pts[0], mul(dts[0], d));
     240    //        EastNorth prevB = add(pts[1], mul(dts[0], d));
     241    //        for(i = 1; i < nodeCount-1; i++) {
     242    //            EastNorth A = add(pts[i], mul(dts[i], d));
     243    //            EastNorth B = add(pts[i+1], mul(dts[i], d));
     244    //            if (Geometry.segmentsParallel(A, B, prevA, prevB)) {
     245    //                ppts[i] = A;
     246    //            } else {
     247    //                ppts[i] = Geometry.getLineLineIntersection(A, B, prevA, prevB);
     248    //            }
     249    //            prevA = A;
     250    //            prevB = B;
     251    //        }
     252    //        if(way.isClosed()) {
     253    //            EastNorth A = add(pts[0], mul(dts[0], d));
     254    //            EastNorth B = add(pts[1], mul(dts[0], d));
     255    //            if (Geometry.segmentsParallel(A, B, prevA, prevB)) {
     256    //                ppts[0] = A;
     257    //            } else {
     258    //                ppts[0] = Geometry.getLineLineIntersection(A, B, prevA, prevB);
     259    //            }
     260    //            ppts[nodeCount-1] = ppts[0];
     261    //        } else {
     262    //            ppts[0] = add(pts[0], mul(dts[0], d));
     263    //            ppts[nodeCount-1] = add(pts[nodeCount-1], mul(dts[nodeCount-2], d));
     264    //        }
     265    //        Way pway = new Way();
     266    //        for(i = 0; i < nodeCount; i++) {
     267    //            Node n = new Node(ppts[i]);
     268    //            Main.main.getCurrentDataSet().addPrimitive(n);
     269    //            pway.addNode(n);
     270    //        }
     271    //        return pway;
     272    //    }
     273
     274    static protected EastNorth mul(EastNorth en, double f) {
     275        return new EastNorth(en.getX()*f, en.getY()*f);
     276    }
     277    static protected EastNorth add(EastNorth a, EastNorth b) {
     278        return new EastNorth(a.east()+b.east(), a.north()+b.north());
     279    }
     280}
  • src/org/openstreetmap/josm/gui/MapFrame.java

    diff --git a/src/org/openstreetmap/josm/gui/MapFrame.java b/src/org/openstreetmap/josm/gui/MapFrame.java
    index 96bddae..aca8e99 100644
    a b import javax.swing.plaf.basic.BasicSplitPaneDivider; 
    3636import javax.swing.plaf.basic.BasicSplitPaneUI;
    3737
    3838import org.openstreetmap.josm.Main;
     39import org.openstreetmap.josm.actions.MakeParallelWay;
    3940import org.openstreetmap.josm.actions.mapmode.DeleteAction;
    4041import org.openstreetmap.josm.actions.mapmode.DrawAction;
    4142import org.openstreetmap.josm.actions.mapmode.ExtrudeAction;
    public class MapFrame extends JPanel implements Destroyable, LayerChangeListener 
    129130        addMapMode(new IconToggleButton(new SelectAction(this)));
    130131        addMapMode(new IconToggleButton(new DrawAction(this)));
    131132        addMapMode(new IconToggleButton(new ExtrudeAction(this)));
     133        addMapMode(new IconToggleButton(new MakeParallelWay(this)));
    132134        addMapMode(new IconToggleButton(new ZoomAction(this)));
    133135        addMapMode(new IconToggleButton(new DeleteAction(this)));
    134136
  • src/org/openstreetmap/josm/tools/Geometry.java

    diff --git a/src/org/openstreetmap/josm/tools/Geometry.java b/src/org/openstreetmap/josm/tools/Geometry.java
    index ade8359..9930fbb 100644
    a b public class Geometry { 
    332332        else
    333333            return new EastNorth(segmentP1.getX() + ldx * offset, segmentP1.getY() + ldy * offset);
    334334    }
     335    public static EastNorth closestPointToLine(EastNorth lineP1, EastNorth lineP2, EastNorth point) {
     336        double ldx = lineP2.getX() - lineP1.getX();
     337        double ldy = lineP2.getY() - lineP1.getY();
     338
     339        if (ldx == 0 && ldy == 0) //segment zero length
     340            return lineP1;
     341
     342        double pdx = point.getX() - lineP1.getX();
     343        double pdy = point.getY() - lineP1.getY();
     344
     345        double offset = (pdx * ldx + pdy * ldy) / (ldx * ldx + ldy * ldy);
     346        return new EastNorth(lineP1.getX() + ldx * offset, lineP1.getY() + ldy * offset);
     347    }
    335348
    336349    /**
    337350     * This method tests if secondNode is clockwise to first node.
    public class Geometry { 
    457470
    458471        return inside;
    459472    }
    460    
     473
    461474    /**
    462475     * returns area of a closed way in square meters
    463476     * (approximate(?), but should be OK for small areas)
    public class Geometry { 
    477490        }
    478491        return Math.abs(area/2);
    479492    }
    480    
     493
    481494    protected static double calcX(Node p1){
    482495        double lat1, lon1, lat2, lon2;
    483496        double dlon, dlat;
    public class Geometry { 
    494507        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    495508        return 6367000 * c;
    496509    }
    497    
     510
    498511    protected static double calcY(Node p1){
    499512        double lat1, lon1, lat2, lon2;
    500513        double dlon, dlat;