Ticket #23444: 23444.patch
File 23444.patch, 11.1 KB (added by , 4 months ago) |
---|
-
src/org/openstreetmap/josm/actions/mapmode/ImproveWayAccuracyHelper.java
3 3 4 4 import java.awt.Point; 5 5 import java.util.List; 6 import java.util.Optional;7 6 8 7 import org.openstreetmap.josm.data.coor.EastNorth; 9 8 import org.openstreetmap.josm.data.osm.Node; … … 43 42 Node node = mv.getNearestNode(p, OsmPrimitive::isSelectable); 44 43 45 44 if (node != null) { 46 Optional<Way> candidate = node.referrers(Way.class).findFirst(); 47 if (candidate.isPresent()) { 48 return candidate.get(); 45 for (Way way : node.getParentWays()) { 46 // We don't want to return ways that are not complete 47 if (way.isUsable()) { 48 return way; 49 } 49 50 } 50 51 } 51 52 … … 69 70 70 71 EastNorth pEN = mv.getEastNorth(p.x, p.y); 71 72 72 Double bestDistance = Double.MAX_VALUE;73 Double currentDistance;73 double bestDistance = Double.MAX_VALUE; 74 double currentDistance; 74 75 List<Pair<Node, Node>> wpps = w.getNodePairs(false); 75 76 76 77 Node result = null; 77 78 78 mainLoop:79 79 for (Node n : w.getNodes()) { 80 80 EastNorth nEN = n.getEastNorth(); 81 81 … … 86 86 87 87 currentDistance = pEN.distance(nEN); 88 88 89 if (currentDistance < bestDistance) { 90 // Making sure this candidate is not behind any segment. 91 for (Pair<Node, Node> wpp : wpps) { 92 if (!wpp.a.equals(n) 93 && !wpp.b.equals(n) 94 && Geometry.getSegmentSegmentIntersection( 95 wpp.a.getEastNorth(), wpp.b.getEastNorth(), 96 pEN, nEN) != null) { 97 continue mainLoop; 98 } 99 } 89 if (currentDistance < bestDistance && ensureCandidateIsNotBehindSegments(wpps, n, pEN, nEN)) { 100 90 result = n; 101 91 bestDistance = currentDistance; 102 92 } … … 106 96 } 107 97 108 98 /** 99 * Check to see if a candidate node is underneath a way segment 100 * 101 * @param wpps The pairs of nodes to check for crossing way segments 102 * @param n The current node to check 103 * @param pEN The cursor east-north position 104 * @param nEN The node east-north position 105 * @return {@code true} if the candidate node is underneath a way segment 106 */ 107 private static boolean ensureCandidateIsNotBehindSegments(Iterable<Pair<Node, Node>> wpps, Node n, EastNorth pEN, EastNorth nEN) { 108 // Making sure this candidate is not behind any segment. 109 for (Pair<Node, Node> wpp : wpps) { 110 if (!wpp.a.equals(n) 111 && !wpp.b.equals(n) 112 && Geometry.getSegmentSegmentIntersection( 113 wpp.a.getEastNorth(), wpp.b.getEastNorth(), 114 pEN, nEN) != null) { 115 return false; 116 } 117 } 118 return true; 119 } 120 121 /** 109 122 * Returns the nearest way segment to cursor. The distance to segment ab is 110 123 * the length of altitude from p to ab (say, c) or the minimum distance from 111 124 * p to a or b if c is out of ab. 112 * 125 * <p> 113 126 * The priority is given to segments where c is in ab. Otherwise, a segment 114 127 * with the largest angle apb is chosen. 115 128 * … … 125 138 126 139 EastNorth pEN = mv.getEastNorth(p.x, p.y); 127 140 128 Double currentDistance; 129 Double currentAngle; 130 Double bestDistance = Double.MAX_VALUE; 131 Double bestAngle = 0.0; 141 double bestDistance = Double.MAX_VALUE; 142 double bestAngle = 0.0; 132 143 133 144 int candidate = -1; 134 145 … … 143 154 144 155 // Finding intersection of the segment with its altitude from p 145 156 EastNorth altitudeIntersection = Geometry.closestPointToSegment(a, b, pEN); 146 currentDistance = pEN.distance(altitudeIntersection);157 final double currentDistance = pEN.distance(altitudeIntersection); 147 158 159 final double currentAngle; 148 160 if (!altitudeIntersection.equals(a) && !altitudeIntersection.equals(b)) { 149 161 // If the segment intersects with the altitude from p, 150 162 // make an angle too big to let this candidate win any others -
test/unit/org/openstreetmap/josm/actions/mapmode/ImproveWayAccuracyActionTest.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.actions.mapmode; 3 3 4 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 4 5 import static org.junit.jupiter.api.Assertions.assertEquals; 6 import static org.junit.jupiter.api.Assertions.assertFalse; 5 7 import static org.junit.jupiter.api.Assertions.assertTrue; 6 8 9 import java.awt.Graphics2D; 10 import java.awt.Point; 11 import java.awt.event.MouseEvent; 12 import java.awt.image.BufferedImage; 13 7 14 import org.junit.jupiter.api.Test; 15 import org.junit.jupiter.api.extension.RegisterExtension; 8 16 import org.openstreetmap.josm.TestUtils; 9 17 import org.openstreetmap.josm.actions.mapmode.ImproveWayAccuracyAction.State; 18 import org.openstreetmap.josm.data.Bounds; 19 import org.openstreetmap.josm.data.coor.ILatLon; 20 import org.openstreetmap.josm.data.coor.LatLon; 10 21 import org.openstreetmap.josm.data.osm.DataSet; 22 import org.openstreetmap.josm.data.osm.Node; 23 import org.openstreetmap.josm.data.osm.Way; 11 24 import org.openstreetmap.josm.gui.MainApplication; 12 25 import org.openstreetmap.josm.gui.MapFrame; 26 import org.openstreetmap.josm.gui.MapView; 13 27 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 28 import org.openstreetmap.josm.gui.util.GuiHelper; 14 29 import org.openstreetmap.josm.testutils.annotations.Main; 15 30 import org.openstreetmap.josm.testutils.annotations.Projection; 31 import org.openstreetmap.josm.testutils.mockers.WindowlessMapViewStateMocker; 32 import org.openstreetmap.josm.testutils.mockers.WindowlessNavigatableComponentMocker; 16 33 34 import mockit.Mock; 35 17 36 /** 18 37 * Unit tests for class {@link ImproveWayAccuracyAction}. 19 38 */ 20 @Main21 39 @Projection 22 40 class ImproveWayAccuracyActionTest { 41 @RegisterExtension 42 Main.MainExtension mainExtension = new Main.MainExtension().setMapViewMocker(SizedWindowlessMapViewStateMocker::new) 43 .setNavigableComponentMocker(SizedWindowlessNavigatableComponentMocker::new); 44 45 private static final int WIDTH = 800; 46 private static final int HEIGHT = 600; 47 private static class SizedWindowlessMapViewStateMocker extends WindowlessMapViewStateMocker { 48 @Mock 49 public int getWidth() { 50 return WIDTH; 51 } 52 53 @Mock 54 public int getHeight() { 55 return HEIGHT; 56 } 57 } 58 59 private static class SizedWindowlessNavigatableComponentMocker extends WindowlessNavigatableComponentMocker { 60 @Mock 61 public int getWidth() { 62 return WIDTH; 63 } 64 65 @Mock 66 public int getHeight() { 67 return HEIGHT; 68 } 69 } 70 71 private static MouseEvent generateEvent(MapView mapView, ILatLon location) { 72 final Point p = mapView.getPoint(location); 73 return new MouseEvent(mapView, 0, 0, 0, p.x, p.y, p.x, p.y, 1, false, MouseEvent.BUTTON1); 74 } 75 23 76 /** 24 77 * Unit test of {@link ImproveWayAccuracyAction#enterMode} and {@link ImproveWayAccuracyAction#exitMode}. 25 78 */ … … 46 99 void testEnumState() { 47 100 TestUtils.superficialEnumCodeCoverage(State.class); 48 101 } 102 103 @Test 104 void testNonRegression23444() { 105 final int width = 800; 106 final int height = 600; 107 final DataSet dataSet = new DataSet(); 108 final OsmDataLayer layer = new OsmDataLayer(dataSet, "ImproveWayAccuracyActionT", null); 109 MainApplication.getLayerManager().addLayer(layer); 110 final ImproveWayAccuracyAction mapMode = new ImproveWayAccuracyAction(); 111 final MapFrame map = MainApplication.getMap(); 112 assertTrue(map.selectMapMode(mapMode)); 113 assertEquals(mapMode, map.mapMode); 114 final Way testWay = TestUtils.newWay("", new Node(1, 1), new Node(2, 1), 115 new Node(3), new Node(4, 1), new Node(5, 1)); 116 testWay.firstNode().setCoor(new LatLon(0, 0)); 117 testWay.lastNode().setCoor(new LatLon(0.001, 0.001)); 118 testWay.getNode(1).setCoor(new LatLon(0.0001, 0.0001)); 119 testWay.getNode(3).setCoor(new LatLon(0.0009, 0.0009)); 120 dataSet.addPrimitiveRecursive(testWay); 121 assertFalse(testWay.getNode(2).isLatLonKnown(), "The second node should not have valid coordinates"); 122 final Graphics2D g2d = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB).createGraphics(); 123 g2d.setClip(0, 0, WIDTH, HEIGHT); 124 try { 125 // If this fails, something else is wrong 126 assertDoesNotThrow(() -> map.mapView.paint(g2d), "The mapview should be able to handle a null coordinate node"); 127 // Ensure that the test way is selected (and use the methods from the action to do so) 128 GuiHelper.runInEDTAndWaitWithException(() -> { 129 map.mapView.zoomTo(new Bounds(0, 0, 0.001, 0.001)); 130 // Get the target way selected (note: not selected in dataset -- use mapMode.mapReleased for that) 131 mapMode.mouseMoved(generateEvent(map.mapView, testWay.getNode(1))); 132 }); 133 // mouseMoved shouldn't cause the way to get selected 134 assertFalse(dataSet.getAllSelected().contains(testWay)); 135 136 // Now check painting (where the problem should occur; the mapMode.paint call should be called as part of the map.mapView.paint call) 137 assertDoesNotThrow(() -> map.mapView.paint(g2d)); 138 assertDoesNotThrow(() -> mapMode.paint(g2d, map.mapView, new Bounds(0, 0, 0.001, 0.001))); 139 140 // Finally, check painting during selection 141 GuiHelper.runInEDTAndWaitWithException(() -> { 142 // Set the way as selected 143 mapMode.mouseReleased(generateEvent(map.mapView, new LatLon(0.0001, 0.0001))); 144 // Set the mouse location (unset in mouseReleased call) 145 mapMode.mouseMoved(generateEvent(map.mapView, new LatLon(0.0001, 0.0001))); 146 }); 147 // The way shouldn't be selected, since it isn't usable for the improve way tool 148 assertFalse(dataSet.getAllSelected().contains(testWay)); 149 // Now check painting again (just in case) 150 assertDoesNotThrow(() -> map.paint(g2d)); 151 assertDoesNotThrow(() -> mapMode.paint(g2d, map.mapView, new Bounds(0, 0, 0.001, 0.001))); 152 } finally { 153 g2d.dispose(); 154 } 155 } 49 156 }