Ticket #18127: 18127.patch

File 18127.patch, 6.7 KB (added by taylor.smock, 7 years ago)

Add check for sharp angles in roads, no unit tests yet

Line 
1Index: src/org/openstreetmap/josm/data/validation/OsmValidator.java
2===================================================================
3--- src/org/openstreetmap/josm/data/validation/OsmValidator.java (revision 15345)
4@@ -61,6 +62,7 @@
5 import org.openstreetmap.josm.data.validation.tests.RelationChecker;
6 import org.openstreetmap.josm.data.validation.tests.RightAngleBuildingTest;
7 import org.openstreetmap.josm.data.validation.tests.SelfIntersectingWay;
8+import org.openstreetmap.josm.data.validation.tests.SharpAngles;
9 import org.openstreetmap.josm.data.validation.tests.SimilarNamedWays;
10 import org.openstreetmap.josm.data.validation.tests.TagChecker;
11 import org.openstreetmap.josm.data.validation.tests.TurnrestrictionTest;
12@@ -148,6 +150,8 @@
13 LongSegment.class, // 3500 .. 3599
14 PublicTransportRouteTest.class, // 3600 .. 3699
15 RightAngleBuildingTest.class, // 3700 .. 3799
16+ SharpAngles.class, // 3800 .. 3899
17 };
18
19 /**
20Index: src/org/openstreetmap/josm/data/validation/tests/SharpAngles.java
21===================================================================
22--- src/org/openstreetmap/josm/data/validation/tests/SharpAngles.java (nonexistent)
23+++ src/org/openstreetmap/josm/data/validation/tests/SharpAngles.java (working copy)
24@@ -0,0 +1,155 @@
25+// License: GPL. For details, see LICENSE file.
26+package org.openstreetmap.josm.data.validation.tests;
27+
28+import static org.openstreetmap.josm.tools.I18n.tr;
29+
30+import java.util.ArrayList;
31+import java.util.Arrays;
32+import java.util.Collection;
33+import java.util.Collections;
34+import java.util.Comparator;
35+import java.util.LinkedHashMap;
36+import java.util.List;
37+import java.util.Map;
38+import java.util.Map.Entry;
39+import java.util.Optional;
40+
41+import org.openstreetmap.josm.data.osm.Node;
42+import org.openstreetmap.josm.data.osm.OsmPrimitive;
43+import org.openstreetmap.josm.data.osm.Way;
44+import org.openstreetmap.josm.data.osm.WaySegment;
45+import org.openstreetmap.josm.data.validation.Severity;
46+import org.openstreetmap.josm.data.validation.Test;
47+import org.openstreetmap.josm.data.validation.TestError;
48+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
49+import org.openstreetmap.josm.tools.Geometry;
50+import org.openstreetmap.josm.tools.Logging;
51+
52+/**
53+ * Find highways that have sharp angles
54+ * @author Taylor Smock
55+ *
56+ */
57+public class SharpAngles extends Test {
58+ private static final int SHARPANGLESCODE = 3900; // TODO change this before commit
59+ /** The code for a sharp angle */
60+ protected static final int SHARP_ANGLES = SHARPANGLESCODE + 0;
61+ /** The maximum angle for sharp angles */
62+ protected static final double MAX_ANGLE = 45.0; // degrees
63+ /** The stepping points for severity */
64+ protected static final Map<Double, Severity> severityBreakPoints = new LinkedHashMap<>();
65+ static {
66+ severityBreakPoints.put(MAX_ANGLE, Severity.OTHER);
67+ severityBreakPoints.put(MAX_ANGLE * 2 / 3, Severity.WARNING);
68+ severityBreakPoints.put(MAX_ANGLE / 3, Severity.ERROR);
69+ }
70+
71+ ArrayList<Way> allWays;
72+ /**
73+ * Construct a new {@code IntersectionIssues} object
74+ */
75+ public SharpAngles() {
76+ super(tr("Sharp angles"), tr("Check for sharp angles on roads"));
77+ }
78+
79+ @Override
80+ public void startTest(ProgressMonitor monitor) {
81+ super.startTest(monitor);
82+ allWays = new ArrayList<>();
83+ }
84+
85+ @Override
86+ public void endTest() {
87+ Way pWay = null;
88+ try {
89+ for (Way way : allWays) {
90+ pWay = way;
91+ checkWayForSharpAngles(way);
92+ }
93+ } catch (Exception e) {
94+ if (pWay != null) {
95+ Logging.debug("Way https://osm.org/way/{0} caused an error ({1})", pWay.getOsmId(), e);
96+ }
97+ Logging.warn(e);
98+ }
99+ allWays = null;
100+ super.endTest();
101+ }
102+
103+ @Override
104+ public void visit(Way way) {
105+ if (!way.isUsable()) return;
106+ if (way.hasKey("highway")) {
107+ allWays.add(way);
108+ }
109+ }
110+
111+ /**
112+ * Check nodes in a way for sharp angles
113+ * @param way A way to check for sharp angles
114+ */
115+ public void checkWayForSharpAngles(Way way) {
116+ Node node1 = null;
117+ Node node2 = null;
118+ Node node3 = null;
119+ int i = -2;
120+ for (Node node : way.getNodes()) {
121+ node1 = node2;
122+ node2 = node3;
123+ node3 = node;
124+ if (node1 != null && node2 != null && node3 != null) {
125+ Double angle = Math.toDegrees(
126+ Math.abs(
127+ Geometry.getCornerAngle(node1.getEastNorth(), node2.getEastNorth(), node3.getEastNorth())));
128+ if (angle < MAX_ANGLE) {
129+ Logging.info("Angle: {0}\r\nWaySegment: {1}, {2}", angle, i-1, i);
130+ List<WaySegment> waysegmentList = Arrays.asList(new WaySegment(way, i), new WaySegment(way, i+1));
131+ Optional<WaySegment> possibleShortSegment = waysegmentList.stream()
132+ .min(Comparator.comparing(segment -> segment.toWay().getLength()));
133+ if (possibleShortSegment.isPresent()) {
134+ createNearlyOverlappingError(angle,
135+ Collections.singleton(way), Collections.singleton(possibleShortSegment.get()));
136+ } else {
137+ createNearlyOverlappingError(angle, Collections.singleton(way), waysegmentList);
138+ }
139+ }
140+ }
141+ i++;
142+ }
143+ }
144+
145+ private void createNearlyOverlappingError(Double angle,
146+ Collection<? extends OsmPrimitive> primitiveIssues, Collection<WaySegment> waysegments) {
147+ TestError.Builder testError = TestError.builder(this, getSeverity(angle), SHARP_ANGLES)
148+ .primitives(primitiveIssues)
149+ .highlightWaySegments(waysegments)
150+ .message(tr("Sharp angle"));
151+ errors.add(testError.build());
152+ }
153+
154+ private Severity getSeverity(Double angle) {
155+ Severity rSeverity = Severity.OTHER;
156+ for (Entry<Double, Severity> entry : severityBreakPoints.entrySet()) {
157+ if (angle < entry.getKey()) {
158+ rSeverity = entry.getValue();
159+ }
160+ }
161+ return rSeverity;
162+ }
163+
164+ @Override
165+ public boolean equals(Object other) {
166+ if (other instanceof SharpAngles) {
167+ SharpAngles otherReal = (SharpAngles) other;
168+ if (otherReal.allWays.equals(allWays)) {
169+ return true;
170+ }
171+ }
172+ return false;
173+ }
174+
175+ @Override
176+ public int hashCode() {
177+ return super.hashCode();
178+ }
179+}