Ticket #18127: 18127.2.patch

File 18127.2.patch, 7.7 KB (added by taylor.smock, 7 years ago)

Ignore some special highway cases, find sharp angles at start/end of closed way. Still no tests, and only highlights the shortest WaySegment.

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