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.ArrayList;
007import java.util.Collection;
008import java.util.Collections;
009
010import org.openstreetmap.josm.data.osm.QuadBuckets;
011import org.openstreetmap.josm.data.osm.Way;
012import org.openstreetmap.josm.data.validation.Severity;
013import org.openstreetmap.josm.data.validation.Test;
014import org.openstreetmap.josm.data.validation.TestError;
015import org.openstreetmap.josm.gui.mappaint.ElemStyles;
016import org.openstreetmap.josm.tools.Geometry;
017import org.openstreetmap.josm.tools.Predicate;
018import org.openstreetmap.josm.tools.Utils;
019
020/**
021 * Checks if areas overlap.
022 * @since 4448
023 */
024public class OverlappingAreas extends Test {
025
026    protected static final int OVERLAPPING_AREAS = 2201;
027    protected final QuadBuckets<Way> index = new QuadBuckets<Way>();
028
029    /**
030     * Constructs a new {@code OverlappingAreas} test.
031     */
032    public OverlappingAreas() {
033        super(tr("Overlapping Areas"), tr("This test checks if areas overlap."));
034    }
035
036    @Override
037    public void visit(Way w) {
038        if (w.isUsable() && w.isArea() && ElemStyles.hasAreaElemStyle(w, false)) {
039            index.add(w);
040        }
041    }
042
043    @Override
044    public void endTest() {
045        for (final Way w : index) {
046            Collection<Way> overlaps = Utils.filter(
047                    index.search(w.getBBox()),
048                    new Predicate<Way>() {
049
050                        @Override
051                        public boolean evaluate(Way wi) {
052                            if (w.equals(wi))
053                                return false;
054                            else
055                                return Geometry.polygonIntersection(w.getNodes(), wi.getNodes())
056                                        == Geometry.PolygonIntersection.CROSSING;
057                        }
058                    });
059            if (!overlaps.isEmpty()) {
060                Collection<Way> overlapsWater = new ArrayList<Way>();
061                Collection<Way> overlapsOther = new ArrayList<Way>();
062
063                String natural1 = w.get("natural");
064                String landuse1 = w.get("landuse");
065                boolean isWaterArea = "water".equals(natural1) || "wetland".equals(natural1) || "coastline".equals(natural1) || "reservoir".equals(landuse1);
066                boolean isWaterArea2 = false;
067
068                for (Way wayOther : overlaps) {
069                    String natural2 = wayOther.get("natural");
070                    String landuse2 = wayOther.get("landuse");
071                    boolean isWaterAreaTest = "water".equals(natural2) || "wetland".equals(natural2) || "coastline".equals(natural2) || "reservoir".equals(landuse2);
072
073                    if (!isWaterArea2) {
074                        isWaterArea2 = isWaterAreaTest;
075                    }
076
077                    if (isWaterArea && isWaterAreaTest) {
078                        overlapsWater.add(wayOther);
079                    } else {
080                        overlapsOther.add(wayOther);
081                    }
082                }
083
084                if (!overlapsWater.isEmpty()) {
085                    errors.add(new TestError(this, Severity.WARNING, tr("Overlapping Water Areas"),
086                            OVERLAPPING_AREAS, Collections.singletonList(w), overlapsWater));
087                }
088
089                if (!overlapsOther.isEmpty()) {
090                    errors.add(new TestError(this, Severity.OTHER, tr("Overlapping Areas"),
091                            OVERLAPPING_AREAS, Collections.singletonList(w), overlapsOther));
092                }
093            }
094        }
095        
096        index.clear();
097
098        super.endTest();
099    }
100
101}