Ticket #10205: 10205-beta.patch
File 10205-beta.patch, 12.9 KB (added by , 5 years ago) |
---|
-
src/org/openstreetmap/josm/actions/AlignInCircleAction.java
29 29 import org.openstreetmap.josm.data.osm.Way; 30 30 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon; 31 31 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.JoinedWay; 32 import org.openstreetmap.josm.data.validation.tests.CrossingWays; 32 33 import org.openstreetmap.josm.data.validation.tests.SelfIntersectingWay; 33 34 import org.openstreetmap.josm.gui.Notification; 34 35 import org.openstreetmap.josm.tools.Geometry; … … 42 43 * @author Petr Dlouhý 43 44 * @author Teemu Koskinen 44 45 * @author Alain Delplanque 46 * @author Gerd Petermann 45 47 * 46 48 * @since 146 47 49 */ … … 136 138 * In all cases, selected nodes are fix, nodes with more than one referrers are fix 137 139 * (first referrer is the selected way) 138 140 * <p> 139 * Case 3: Only nodes are selected140 * --> Align these nodes, all are fix141 141 * @param ds data set in which the command operates 142 142 * @return the resulting command to execute to perform action 143 143 * @throws InvalidSelection if selection cannot be used … … 146 146 */ 147 147 public static Command buildCommand(DataSet ds) throws InvalidSelection { 148 148 Collection<OsmPrimitive> sel = ds.getSelected(); 149 List<Node> nodes = new LinkedList<>();149 List<Node> selectedNodes = new LinkedList<>(); 150 150 // fixNodes: All nodes for which the angle relative to center should not be modified 151 Set<Node> fixNodes = new HashSet<>();152 151 List<Way> ways = new LinkedList<>(); 153 EastNorth center = null;154 double radius = 0;155 152 156 153 for (OsmPrimitive osm : sel) { 157 154 if (osm instanceof Node) { 158 nodes.add((Node) osm);155 selectedNodes.add((Node) osm); 159 156 } else if (osm instanceof Way) { 160 157 ways.add((Way) osm); 161 158 } 162 159 } 160 if (ways.isEmpty()) { 161 throw new InvalidSelection(tr("Please select at least one way.")); 162 } 163 163 164 Set<Node> fixNodes = new HashSet<>(); 165 EastNorth center = null; 166 double radius = 0; 167 final List<Node> nodes; 164 168 if (ways.size() == 1 && !ways.get(0).isClosed()) { 165 169 // Case 1 166 170 Way w = ways.get(0); 167 if (SelfIntersectingWay.isSelfIntersecting(w)) {168 throw new InvalidSelection(tr("Self-intersecting way"));169 }170 171 171 fixNodes.add(w.firstNode()); 172 172 fixNodes.add(w.lastNode()); 173 fixNodes.addAll(nodes); 174 fixNodes.addAll(collectNodesWithExternReferrers(ways)); 173 fixNodes.addAll(selectedNodes); 175 174 // Temporary closed way used to reorder nodes 176 175 Way closedWay = new Way(w); 177 176 closedWay.addNode(w.firstNode()); 178 177 nodes = collectNodesAnticlockwise(Collections.singletonList(closedWay)); 179 178 closedWay.setNodes(null); // see #19885 180 } else if ( !ways.isEmpty() &&checkWaysArePolygon(ways)) {179 } else if (checkWaysArePolygon(ways)) { 181 180 // Case 2 181 // nodes on selected ways 182 182 List<Node> inside = new ArrayList<>(); 183 183 List<Node> outside = new ArrayList<>(); 184 184 185 for (Node n: nodes) { 186 boolean isInside = ways.stream().anyMatch(w -> w.getNodes().contains(n)); 187 if (isInside) 185 for (Node n: selectedNodes) { 186 if (ways.stream().anyMatch(w -> w.containsNode(n))) 188 187 inside.add(n); 189 188 else 190 189 outside.add(n); … … 204 203 } 205 204 206 205 fixNodes.addAll(inside); 207 fixNodes.addAll(collectNodesWithExternReferrers(ways));208 206 nodes = collectNodesAnticlockwise(ways); 209 if (nodes.size() < 4) {210 throw new InvalidSelection(tr("Not enough nodes in selected ways."));211 }212 } else if (ways.isEmpty() && nodes.size() > 3) {213 // Case 3214 fixNodes.addAll(nodes);215 // No need to reorder nodes since all are fix216 207 } else { 217 if (ways.isEmpty() && nodes.size() <= 3)218 throw new InvalidSelection(tr("Please select at least four nodes."));219 208 throw new InvalidSelection(); 220 209 } 210 fixNodes.addAll(collectNodesWithExternReferrers(ways)); 221 211 212 if (nodes.size() < 4) { 213 throw new InvalidSelection(tr("Not enough nodes in selected ways.")); 214 } 222 215 // Check if one or more nodes are outside of download area 223 216 if (nodes.stream().anyMatch(Node::isOutsideDownloadArea)) 224 217 throw new InvalidSelection(tr("One or more nodes involved in this action is outside of the downloaded area.")); 225 218 219 220 226 221 if (center == null) { 227 // Compute the center of nodes 228 center = Geometry.getCenter(nodes); 222 if (validateGeometry(nodes)) { 223 // Compute the center of nodes 224 center = Geometry.getCenter(nodes); 225 } 229 226 if (center == null) { 230 throw new InvalidSelection(tr("Cannot determine center of selected nodes."));227 throw new InvalidSelection(tr("Cannot determine center of circle for this geometry.")); 231 228 } 232 229 } 230 Logging.debug("center: {0}", new Node(center).getCoor().toString()); 233 231 234 232 // Now calculate the average distance to each node from the 235 233 // center. This method is ok as long as distances are short … … 285 283 return new SequenceCommand(tr("Align Nodes in Circle"), cmds); 286 284 } 287 285 286 private static boolean validateGeometry(List<Node> nodes) { 287 Way test = new Way(); 288 try { 289 test.setNodes(nodes); 290 if (!test.isClosed()) 291 test.addNode(test.firstNode()); 292 if (CrossingWays.isSelfCrossing(test)) 293 return false; 294 return !SelfIntersectingWay.isSelfIntersecting(test); 295 } finally { 296 test.setNodes(null); // see #19855 297 } 298 } 299 288 300 /** 289 301 * Collect all nodes with more than one referrer. 290 302 * @param ways Ways from witch nodes are selected … … 343 355 } 344 356 return true; 345 357 } 346 347 358 } -
src/org/openstreetmap/josm/data/validation/tests/CrossingWays.java
26 26 import org.openstreetmap.josm.data.validation.TestError; 27 27 import org.openstreetmap.josm.data.validation.util.ValUtil; 28 28 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 29 import org.openstreetmap.josm.tools.CheckParameterUtil; 29 30 import org.openstreetmap.josm.tools.Logging; 30 31 31 32 /** … … 468 469 } 469 470 } 470 471 472 /** 473 * Check if the given way is self crossing 474 * @param way the way to check 475 * @return {@code true} if one or more segments of the way are crossing 476 * @see SelfIntersectingWay 477 * @since xxx 478 */ 479 public static boolean isSelfCrossing(Way way) { 480 CheckParameterUtil.ensureParameterNotNull(way, "way"); 481 SelfCrossing test = new SelfCrossing(); 482 test.visit(way); 483 return !test.getErrors().isEmpty(); 484 } 471 485 } -
test/data/regress/20041/circle-action.osm
1 <?xml version='1.0' encoding='UTF-8'?> 2 <osm version='0.6' generator='JOSM'> 3 <node id='3353142168' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90029704945' lon='8.44321406009' /> 4 <node id='3353142170' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90031807489' lon='8.44326193915' /> 5 <node id='3353142171' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90030468523' lon='8.44306677855' /> 6 <node id='3353142174' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90033729149' lon='8.44301884234' /> 7 <node id='3353142175' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90034976283' lon='8.44329321571' /> 8 <node id='3353142183' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90040118461' lon='8.44329685665' /> 9 <node id='3353142184' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.9004078959' lon='8.44300955386' /> 10 <node id='3353142189' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90046511808' lon='8.44319779138' /> 11 <node id='3353142190' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90044790476' lon='8.44305434646' /> 12 <node id='3353142192' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90046284581' lon='8.44309505092' /> 13 <way id='-104527' action='modify' visible='true'> 14 <nd ref='3353142171' /> 15 <nd ref='3353142168' /> 16 <nd ref='3353142170' /> 17 <nd ref='3353142175' /> 18 <nd ref='3353142183' /> 19 <nd ref='3353142189' /> 20 <nd ref='3353142192' /> 21 <nd ref='3353142190' /> 22 <nd ref='3353142184' /> 23 <nd ref='3353142174' /> 24 <nd ref='3353142171' /> 25 <tag k='barrier' v='fence' /> 26 </way> 27 <way id='328483730' timestamp='2015-02-15T23:33:35Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323'> 28 <nd ref='3353142174' /> 29 <nd ref='3353142184' /> 30 <nd ref='3353142190' /> 31 <nd ref='3353142192' /> 32 <nd ref='3353142189' /> 33 <nd ref='3353142183' /> 34 <nd ref='3353142175' /> 35 <nd ref='3353142170' /> 36 <nd ref='3353142168' /> 37 <nd ref='3353142171' /> 38 <nd ref='3353142174' /> 39 <tag k='natural' v='water' /> 40 </way> 41 </osm> -
test/unit/org/openstreetmap/josm/actions/AlignInCircleActionTest.java
92 92 } 93 93 94 94 /** 95 * Test case: way with several nodes selected96 * @throws Exception if an error occurs97 */98 @Test99 void testNodesSelected() throws Exception {100 DataSet ds = OsmReader.parseDataSet(Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "alignCircleBefore.osm")), null);101 DataSet ds2 = OsmReader.parseDataSet(Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "alignCircleAfter2.osm")), null);102 103 Way circularWay = null;104 for (Way w : ds.getWays()) {105 if ("roundabout".equals(w.get("junction"))) {106 circularWay = w;107 break;108 }109 }110 assertNotNull(circularWay);111 if (circularWay != null) {112 ds.setSelected(circularWay.getNodes());113 Command c = AlignInCircleAction.buildCommand(ds);114 assertNotNull(c);115 c.executeCommand();116 Way expected = (Way) ds2.getPrimitiveById(circularWay);117 assertNotNull(expected);118 assertEquals(expected, circularWay);119 assertEquals(expected.getNodesCount(), circularWay.getNodesCount());120 for (Node n1 : circularWay.getNodes()) {121 Node n2 = (Node) ds2.getPrimitiveById(n1);122 assertEquals(n1.lat(), n2.lat(), 1e-5);123 assertEquals(n1.lon(), n2.lon(), 1e-5);124 }125 }126 }127 128 /**129 95 * Test case: original roundabout was split, two ways selected, they build a closed ring 130 96 * @throws Exception if an error occurs 131 97 */ … … 149 115 } 150 116 } 151 117 118 /** 119 * Test case: original roundabout was split, two ways selected, they build a closed ring 120 * @throws Exception if an error occurs 121 */ 122 @Test 123 void testPShapedWay() throws Exception { 124 DataSet ds = new DataSet(); 125 126 } 152 127 }