source: josm/trunk/src/org/openstreetmap/josm/data/validation/tests/DirectionNodes.java@ 17411

Last change on this file since 17411 was 17411, checked in by GerdP, 3 years ago

fix #20019: Warn about direction=forward/backward on invalid nodes.

  • show error for unconnected node Unconnected node with {0}. Use angle or cardinal direction
  • show warning for node that is connected, but not to a suitable way Node with {0} should be connected to a linear way
  • show information for Node with {0} on end of way and Node with {0} on a connection of multiple ways, both in group Disputed usage of direction on node
  • special handling for highways: if there is a major highway as defined in Highways.CLASSIFIED_HIGHWAYS, ignore minor ways like footway, path to reduce false positives at traffic lights.
File size: 4.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.validation.tests;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.util.ArrayList;
8import java.util.List;
9import java.util.Map.Entry;
10import java.util.stream.Collectors;
11
12import org.openstreetmap.josm.data.osm.Node;
13import org.openstreetmap.josm.data.osm.OsmPrimitive;
14import org.openstreetmap.josm.data.osm.Way;
15import org.openstreetmap.josm.data.validation.Severity;
16import org.openstreetmap.josm.data.validation.Test;
17import org.openstreetmap.josm.data.validation.TestError;
18
19/**
20 * Find nodes with direction tag and invalid number of parent ways or position in way. See #20019.
21 * @author Gerd Petermann
22 * @since 17349
23 */
24public class DirectionNodes extends Test {
25 private static final int MULTIPLE_WAYS_CODE = 4000;
26 private static final int END_NODE_CODE = 4001;
27 private static final int NO_WAY_CODE = 4002;
28 private static final int NO_SUITABLE_WAY = 4003;
29
30 private static final String INVALID_USE_MSG = tr("Invalid usage of direction on node");
31 private static final String DISPUTED_USE_MSG = tr("Disputed usage of direction on node");
32
33 /**
34 * Construct a new {@code DirectionNodes} object
35 */
36 public DirectionNodes() {
37 super(tr("Direction nodes"), tr("Check for nodes which have a ''forward'' or ''backward'' direction"));
38 }
39
40 @Override
41 public void visit(Node n) {
42 if (!n.isUsable() || !n.isTagged())
43 return;
44 for (Entry<String, String> tag : n.getKeys().entrySet()) {
45 if (("forward".equals(tag.getValue()) || "backward".equals(tag.getValue()))
46 && ("direction".equals(tag.getKey()) || tag.getKey().endsWith(":direction"))) {
47 checkParents(n, tag.toString());
48 }
49 }
50 }
51
52 private static boolean isSuitableParentWay(Way w) {
53 return w.hasKey("highway", "railway", "waterway") || w.hasTag("man_made", "pipeline");
54 }
55
56 private void checkParents(Node n, String tag) {
57 final List<Way> ways = new ArrayList<>();
58 int count = 0;
59 int countHighWays = 0;
60 for (Way w : n.getParentWays()) {
61 if (isSuitableParentWay(w)) {
62 ways.add(w);
63 if (w.hasKey("highway"))
64 countHighWays++;
65 }
66 count++;
67 }
68
69 // ignore minor highways (footway, path etc) if a major highway is found
70 if (countHighWays > 1 && (n.hasKey("highway") || n.hasTag("traffic_sign", "city_limit"))) {
71 List<Way> minor = ways.stream().filter(w -> !w.hasTag("highway", Highways.CLASSIFIED_HIGHWAYS))
72 .collect(Collectors.toList());
73 if (minor.size() != countHighWays) {
74 ways.removeAll(minor);
75 }
76 }
77 boolean needsParentWays = n.isNew()
78 || (!n.isOutsideDownloadArea() && n.getDataSet().getDataSourceArea() != null);
79 TestError.Builder builder = null;
80 if (ways.isEmpty() && needsParentWays) {
81 if (count == 0) {
82 builder = TestError.builder(this, Severity.ERROR, NO_WAY_CODE).message(INVALID_USE_MSG,
83 marktr("Unconnected node with {0}. Use angle or cardinal direction"), tag);
84 } else {
85 builder = TestError.builder(this, Severity.WARNING, NO_SUITABLE_WAY).message(INVALID_USE_MSG,
86 marktr("Node with {0} should be connected to a linear way"), tag);
87 }
88 } else if (ways.size() == 1) {
89 Way w = ways.get(0);
90 if (w.firstNode() == n || w.lastNode() == n) {
91 builder = TestError.builder(this, Severity.OTHER, END_NODE_CODE).message(DISPUTED_USE_MSG,
92 marktr("Node with {0} on end of way"), tag);
93 }
94 } else if (ways.size() > 1) {
95 builder = TestError.builder(this, Severity.OTHER, MULTIPLE_WAYS_CODE).message(DISPUTED_USE_MSG,
96 marktr("Node with {0} on a connection of multiple ways"), tag);
97 }
98 if (builder != null) {
99 List<OsmPrimitive> primitives = new ArrayList<>();
100 primitives.add(n);
101 primitives.addAll(ways);
102 errors.add(builder.primitives(primitives).highlight(n).build());
103 }
104 }
105}
Note: See TracBrowser for help on using the repository browser.