001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.validation.tests; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.util.Collections; 007import java.util.List; 008 009import org.openstreetmap.josm.actions.OrthogonalizeAction; 010import org.openstreetmap.josm.actions.OrthogonalizeAction.InvalidUserInputException; 011import org.openstreetmap.josm.data.osm.Node; 012import org.openstreetmap.josm.data.osm.Way; 013import org.openstreetmap.josm.data.validation.Severity; 014import org.openstreetmap.josm.data.validation.Test; 015import org.openstreetmap.josm.data.validation.TestError; 016import org.openstreetmap.josm.gui.progress.ProgressMonitor; 017import org.openstreetmap.josm.spi.preferences.Config; 018import org.openstreetmap.josm.tools.Logging; 019import org.openstreetmap.josm.tools.Pair; 020 021/** 022 * Checks for buildings with angles close to right angle. 023 * 024 * @author marxin 025 * @since 13670 026 */ 027public class RightAngleBuildingTest extends Test { 028 029 /** Maximum angle difference from right angle that is considered as invalid. */ 030 protected double maxAngleDelta; 031 032 /** Minimum angle difference from right angle that is considered as invalid. */ 033 protected double minAngleDelta; 034 035 /** 036 * Constructs a new {@code RightAngleBuildingTest} test. 037 */ 038 public RightAngleBuildingTest() { 039 super(tr("Almost right angle buildings"), 040 tr("Checks for buildings that have angles close to right angle and are not orthogonalized.")); 041 } 042 043 @Override 044 public void visit(Way w) { 045 if (!w.isUsable() || !w.isClosed() || !isBuilding(w)) return; 046 047 List<Pair<Double, Node>> angles = w.getAngles(); 048 for (Pair<Double, Node> pair: angles) { 049 if (checkAngle(pair.a)) { 050 TestError.Builder builder = TestError.builder(this, Severity.WARNING, 3701) 051 .message(tr("Building with an almost square angle")) 052 .primitives(w) 053 .highlight(pair.b); 054 if (angles.stream().noneMatch( 055 p -> Math.abs(p.a - 90) >= maxAngleDelta && Math.abs(p.a - 180) >= minAngleDelta)) { 056 builder.fix(() -> { 057 try { 058 return OrthogonalizeAction.orthogonalize(Collections.singleton(w)); 059 } catch (InvalidUserInputException e) { 060 Logging.warn(e); 061 return null; 062 } 063 }); 064 } 065 errors.add(builder.build()); 066 return; 067 } 068 } 069 } 070 071 @Override 072 public void startTest(ProgressMonitor monitor) { 073 super.startTest(monitor); 074 maxAngleDelta = Config.getPref().getDouble("validator.RightAngleBuilding.maximumDelta", 10.0); 075 minAngleDelta = Config.getPref().getDouble("validator.RightAngleBuilding.minimumDelta", 0.25); 076 } 077 078 private boolean checkAngle(double angle) { 079 double difference = Math.abs(angle - 90); 080 return difference > minAngleDelta && difference < maxAngleDelta; 081 } 082}