Ticket #67: make-parallel-way.patch

File make-parallel-way.patch, 14.5 KB (added by olejorgenb, 12 months 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;