From 9a3aaeb5fd7f4473c057cce17d51f0b93b6eb8a3 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Sun, 4 Mar 2018 10:25:28 +0100
Subject: [PATCH 2/2] Introduce almost square check for buildings.

---
 src/org/openstreetmap/josm/data/osm/Way.java       | 32 +++++++++++
 .../josm/data/validation/OsmValidator.java         |  2 +
 .../validation/tests/RightAngleBuildingTest.java   | 64 ++++++++++++++++++++++
 3 files changed, 98 insertions(+)
 create mode 100644 src/org/openstreetmap/josm/data/validation/tests/RightAngleBuildingTest.java

diff --git a/src/org/openstreetmap/josm/data/osm/Way.java b/src/org/openstreetmap/josm/data/osm/Way.java
index 8fd0cef49..4203f7f71 100644
--- a/src/org/openstreetmap/josm/data/osm/Way.java
+++ b/src/org/openstreetmap/josm/data/osm/Way.java
@@ -15,6 +15,8 @@ import org.openstreetmap.josm.data.osm.visitor.OsmPrimitiveVisitor;
 import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.tools.CopyList;
+import org.openstreetmap.josm.tools.Geometry;
+import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.Pair;
 import org.openstreetmap.josm.tools.Utils;
 
@@ -805,4 +807,34 @@ public final class Way extends OsmPrimitive implements IWay {
             n.clearCachedStyle();
         }
     }
+
+    private double getNormalizedAngleInDegrees(double angle) {
+        angle = Math.abs(180 * angle / Math.PI);
+        return angle;
+    }
+
+    public List<Pair<Double, Node>> getAngles() {
+        List<Pair<Double, Node>> angles = new ArrayList<Pair<Double, Node>>();
+
+        List<Node> nodes = getNodes();
+        int count = getNodesCount();
+
+        for (int i = 1; i < count - 1; i++)
+        {
+            Node n0 = nodes.get(i - 1);
+            Node n1 = nodes.get(i);
+            Node n2 = nodes.get(i + 1);
+
+            double angle = getNormalizedAngleInDegrees(Geometry.getCornerAngle(n0.getEastNorth(), n1.getEastNorth(),
+                    n2.getEastNorth()));
+            angles.add(new Pair<Double, Node>(angle, n1));
+        }
+
+        angles.add(new Pair<Double, Node>(getNormalizedAngleInDegrees(Geometry.getCornerAngle(
+                nodes.get(count - 2).getEastNorth(),
+                nodes.get(0).getEastNorth(),
+                nodes.get(1).getEastNorth())), nodes.get(0)));
+
+        return angles;
+    }
 }
diff --git a/src/org/openstreetmap/josm/data/validation/OsmValidator.java b/src/org/openstreetmap/josm/data/validation/OsmValidator.java
index 30a2661fe..4cf70c621 100644
--- a/src/org/openstreetmap/josm/data/validation/OsmValidator.java
+++ b/src/org/openstreetmap/josm/data/validation/OsmValidator.java
@@ -52,6 +52,7 @@ import org.openstreetmap.josm.data.validation.tests.OverlappingWays;
 import org.openstreetmap.josm.data.validation.tests.PowerLines;
 import org.openstreetmap.josm.data.validation.tests.PublicTransportRouteTest;
 import org.openstreetmap.josm.data.validation.tests.RelationChecker;
+import org.openstreetmap.josm.data.validation.tests.RightAngleBuildingTest;
 import org.openstreetmap.josm.data.validation.tests.SelfIntersectingWay;
 import org.openstreetmap.josm.data.validation.tests.SimilarNamedWays;
 import org.openstreetmap.josm.data.validation.tests.TagChecker;
@@ -139,6 +140,7 @@ public final class OsmValidator {
         ApiCapabilitiesTest.class, // 3400 .. 3499
         LongSegment.class, // 3500 .. 3599
         PublicTransportRouteTest.class, // 3600 .. 3699
+        RightAngleBuildingTest.class, // 3700 .. 3799
     };
 
     /**
diff --git a/src/org/openstreetmap/josm/data/validation/tests/RightAngleBuildingTest.java b/src/org/openstreetmap/josm/data/validation/tests/RightAngleBuildingTest.java
new file mode 100644
index 000000000..3779dfc84
--- /dev/null
+++ b/src/org/openstreetmap/josm/data/validation/tests/RightAngleBuildingTest.java
@@ -0,0 +1,64 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.validation.tests;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.validation.Severity;
+import org.openstreetmap.josm.data.validation.Test;
+import org.openstreetmap.josm.data.validation.TestError;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.tools.Pair;
+
+/**
+ * Checks for buildings with angles close to right angle.
+ */
+public class RightAngleBuildingTest extends Test {
+
+    /** Maximum angle difference from right angle that is considered as invalid. */
+    protected double maxAngleDelta;
+
+    /** Minimum angle difference from right angle that is considered as invalid. */
+    protected double minAngleDelta;
+
+    /**
+     * Constructs a new {@code RightAngleBuildingTest} test.
+     */
+    public RightAngleBuildingTest() {
+        super(tr("Almost right angle buildings"),
+                tr("Checks for buildings that have angles close to right angle and are not orthogonalized."));
+    }
+
+    @Override
+    public void visit(Way w) {
+        if (!w.isUsable() || !w.isClosed() || !isBuilding(w)) return;
+
+        for (Pair<Double, Node> pair: w.getAngles())
+            if (!checkAngle(w, pair.a, pair.b))
+                return;
+    }
+
+    @Override
+    public void startTest(ProgressMonitor monitor) {
+        super.startTest(monitor);
+        maxAngleDelta = Config.getPref().getDouble("validator.RightAngleBuilding.maximumDelta", 10.0);
+        minAngleDelta = Config.getPref().getDouble("validator.RightAngleBuilding.minimumDelta", 0.001);
+    }
+
+    private boolean checkAngle(Way w, double angle, Node n) {
+        double difference = Math.abs(angle - 90);
+
+        if (difference > minAngleDelta && difference < maxAngleDelta) {
+            errors.add(TestError.builder(this, Severity.WARNING, 3701)
+                    .message(tr("Building with an almost square angle"))
+                    .primitives(w)
+                    .highlight(n)
+                    .build());
+            return false;
+        }
+
+        return true;
+    }
+}
-- 
2.16.3

