[8382] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
[8320] | 2 | package org.openstreetmap.josm.data.validation.tests;
|
---|
| 3 |
|
---|
[11129] | 4 | import static org.openstreetmap.josm.tools.I18n.marktr;
|
---|
[8320] | 5 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
| 6 |
|
---|
[12037] | 7 | import java.util.Collections;
|
---|
[12032] | 8 | import java.util.HashSet;
|
---|
| 9 | import java.util.Set;
|
---|
| 10 |
|
---|
[8320] | 11 | import org.openstreetmap.josm.Main;
|
---|
[12037] | 12 | import org.openstreetmap.josm.data.coor.LatLon;
|
---|
[12032] | 13 | import org.openstreetmap.josm.data.osm.Node;
|
---|
[8320] | 14 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
---|
| 15 | import org.openstreetmap.josm.data.osm.Way;
|
---|
[12037] | 16 | import org.openstreetmap.josm.data.osm.WaySegment;
|
---|
[8320] | 17 | import org.openstreetmap.josm.data.validation.Severity;
|
---|
| 18 | import org.openstreetmap.josm.data.validation.Test;
|
---|
| 19 | import org.openstreetmap.josm.data.validation.TestError;
|
---|
| 20 | import org.openstreetmap.josm.gui.progress.ProgressMonitor;
|
---|
| 21 |
|
---|
| 22 | /**
|
---|
[12032] | 23 | * Checks for very long segments.
|
---|
[8320] | 24 | *
|
---|
| 25 | * @since 8320
|
---|
| 26 | */
|
---|
| 27 | public class LongSegment extends Test {
|
---|
| 28 |
|
---|
| 29 | /** Long segment error */
|
---|
[10378] | 30 | protected static final int LONG_SEGMENT = 3501;
|
---|
[8320] | 31 | /** Maximum segment length for this test */
|
---|
| 32 | protected int maxlength;
|
---|
[12037] | 33 | /** set of visited ways. Tracking this increases performance when checking single nodes. */
|
---|
| 34 | private Set<Way> visitedWays;
|
---|
[8320] | 35 |
|
---|
[12037] | 36 | /** set of way segments that have been reported */
|
---|
| 37 | protected Set<WaySegment> reported;
|
---|
| 38 |
|
---|
[8320] | 39 | /**
|
---|
| 40 | * Constructor
|
---|
| 41 | */
|
---|
| 42 | public LongSegment() {
|
---|
| 43 | super(tr("Long segments"),
|
---|
[8334] | 44 | tr("This tests for long way segments, which are usually errors."));
|
---|
[8320] | 45 | }
|
---|
| 46 |
|
---|
| 47 | @Override
|
---|
[12032] | 48 | public void visit(Node n) {
|
---|
[12037] | 49 | // Test all way segments around this node.
|
---|
| 50 | // If there is an error in the unchanged part of the way, we do not need to warn the user about it.
|
---|
| 51 | for (Way way : n.getParentWays()) {
|
---|
| 52 | if (ignoreWay(way)) {
|
---|
| 53 | continue;
|
---|
| 54 | }
|
---|
| 55 | // Do not simply use index of - a node may be in a way multiple times
|
---|
| 56 | for (int i = 0; i < way.getNodesCount(); i++) {
|
---|
| 57 | if (n == way.getNode(i)) {
|
---|
| 58 | if (i > 0) {
|
---|
| 59 | visitWaySegment(way, i - 1);
|
---|
| 60 | }
|
---|
| 61 | if (i < way.getNodesCount() - 1) {
|
---|
| 62 | visitWaySegment(way, i);
|
---|
| 63 | }
|
---|
| 64 | }
|
---|
| 65 | }
|
---|
[12032] | 66 | }
|
---|
| 67 | }
|
---|
| 68 |
|
---|
| 69 | @Override
|
---|
[8320] | 70 | public void visit(Way w) {
|
---|
[12037] | 71 | if (ignoreWay(w)) {
|
---|
| 72 | return;
|
---|
| 73 | }
|
---|
| 74 | visitedWays.add(w);
|
---|
| 75 |
|
---|
[12032] | 76 | testWay(w);
|
---|
| 77 | }
|
---|
| 78 |
|
---|
| 79 | private void testWay(Way w) {
|
---|
[12037] | 80 | for (int i = 0; i < w.getNodesCount() - 1; i++) {
|
---|
| 81 | visitWaySegment(w, i);
|
---|
[10080] | 82 | }
|
---|
[12037] | 83 | }
|
---|
| 84 |
|
---|
| 85 | private boolean ignoreWay(Way w) {
|
---|
| 86 | return visitedWays.contains(w) || w.hasTag("route", "ferry");
|
---|
| 87 | }
|
---|
| 88 |
|
---|
| 89 | private void visitWaySegment(Way w, int i) {
|
---|
| 90 | LatLon coor1 = w.getNode(i).getCoor();
|
---|
| 91 | LatLon coor2 = w.getNode(i + 1).getCoor();
|
---|
| 92 |
|
---|
| 93 | if (coor1 != null && coor2 != null) {
|
---|
| 94 | Double length = coor1.greatCircleDistance(coor2);
|
---|
| 95 | if (length > maxlength) {
|
---|
| 96 | addErrorForSegment(new WaySegment(w, i), length / 1000.0);
|
---|
| 97 | }
|
---|
| 98 | }
|
---|
| 99 | }
|
---|
| 100 |
|
---|
| 101 | private void addErrorForSegment(WaySegment waySegment, Double length) {
|
---|
| 102 | if (reported.add(waySegment)) {
|
---|
[11129] | 103 | errors.add(TestError.builder(this, Severity.WARNING, LONG_SEGMENT)
|
---|
| 104 | .message(tr("Long segments"), marktr("Very long segment of {0} kilometers"), length.intValue())
|
---|
[12037] | 105 | .primitives(waySegment.way)
|
---|
| 106 | .highlightWaySegments(Collections.singleton(waySegment))
|
---|
[11129] | 107 | .build());
|
---|
[8320] | 108 | }
|
---|
| 109 | }
|
---|
| 110 |
|
---|
| 111 | @Override
|
---|
| 112 | public void startTest(ProgressMonitor monitor) {
|
---|
| 113 | super.startTest(monitor);
|
---|
[11100] | 114 | maxlength = Main.pref.getInteger("validator.maximum.segment.length", 15_000);
|
---|
[12037] | 115 | reported = new HashSet<>();
|
---|
| 116 | visitedWays = new HashSet<>();
|
---|
[8320] | 117 | }
|
---|
| 118 |
|
---|
| 119 | @Override
|
---|
[12032] | 120 | public void endTest() {
|
---|
| 121 | super.endTest();
|
---|
[12037] | 122 | // free memory
|
---|
| 123 | visitedWays = null;
|
---|
| 124 | reported = null;
|
---|
[12032] | 125 | }
|
---|
| 126 |
|
---|
| 127 | @Override
|
---|
[8320] | 128 | public boolean isPrimitiveUsable(OsmPrimitive p) {
|
---|
[12040] | 129 | return p.isUsable() && (isUsableWay(p) || isUsableNode(p));
|
---|
[8320] | 130 | }
|
---|
[12040] | 131 |
|
---|
[12050] | 132 | private static boolean isUsableNode(OsmPrimitive p) {
|
---|
[12040] | 133 | // test changed nodes - ways referred by them may not be checked automatically.
|
---|
| 134 | return p instanceof Node && p.isDrawable();
|
---|
| 135 | }
|
---|
| 136 |
|
---|
[12050] | 137 | private static boolean isUsableWay(OsmPrimitive p) {
|
---|
[12040] | 138 | // test only Ways with at least 2 nodes
|
---|
| 139 | return p instanceof Way && ((Way) p).getNodesCount() > 1;
|
---|
| 140 | }
|
---|
[8320] | 141 | }
|
---|