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.Arrays;
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) || !IN_DOWNLOADED_AREA_STRICT.test(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.OTHER, 3701)
051                                                     .message(tr("Building with an almost square angle"))
052                                                     .primitives(w)
053                                                     .highlight(pair.b);
054                builder.fix(() -> {
055                    try {
056                        return OrthogonalizeAction.orthogonalize(Arrays.asList(w, pair.b));
057                    } catch (InvalidUserInputException e) {
058                        Logging.warn(e);
059                        return null;
060                    }
061                });
062                errors.add(builder.build());
063                return;
064            }
065        }
066    }
067
068    @Override
069    public void startTest(ProgressMonitor monitor) {
070        super.startTest(monitor);
071        maxAngleDelta = Config.getPref().getDouble("validator.RightAngleBuilding.maximumDelta", 10.0);
072        minAngleDelta = Config.getPref().getDouble("validator.RightAngleBuilding.minimumDelta", 1.0);
073    }
074
075    private boolean checkAngle(double angle) {
076        double difference = Math.abs(angle - 90);
077        return difference > minAngleDelta && difference < maxAngleDelta;
078    }
079}