Ignore:
Timestamp:
2020-02-29T08:07:39+01:00 (5 years ago)
Author:
GerdP
Message:

fix #13165 Validator did not warn about overlapping multipolygons

  • let CrossingFinder accept multipolygons
  • improve highlighting of overlapping areas
  • if incomplete multipolygons are tested the area calculation is used unless open ends are found, else the same algortihm as in CrossingWays is used to hilite the crossing segments. In the later case overlaps with shared nodes are not (yet) found.
Location:
trunk/src/org/openstreetmap/josm/gui/mappaint
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/mappaint/Environment.java

    r15938 r15959  
    33
    44import java.awt.geom.Area;
    5 import java.util.HashMap;
    65import java.util.LinkedHashSet;
     6import java.util.List;
    77import java.util.Map;
    88import java.util.Set;
     
    1010import org.openstreetmap.josm.data.osm.IPrimitive;
    1111import org.openstreetmap.josm.data.osm.Relation;
     12import org.openstreetmap.josm.data.osm.Way;
     13import org.openstreetmap.josm.data.osm.WaySegment;
    1214import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.Context;
    1315import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.LinkSelector;
     
    7274
    7375    /**
     76     * Crossing ways result from CrossingFinder, filled for incomplete ways/relations
     77    */
     78    public Map<IPrimitive, Map<List<Way>, List<WaySegment>>> crossingWaysMap;
     79
     80    /**
    7481     * Intersection areas (only filled with CrossingFinder if children is not null)
    7582     */
    7683    public Map<IPrimitive, Area> intersections;
     84
     85    /**
     86     * Cache for multipolygon areas, can be null, used with CrossingFinder
     87     */
     88    public Map<IPrimitive, Area> mpAreaCache;
    7789
    7890    /**
     
    126138        this.context = other.getContext();
    127139        this.children = other.children == null ? null : new LinkedHashSet<>(other.children);
    128         this.intersections = other.intersections == null ? null : new HashMap<>(other.intersections);
     140        this.intersections = other.intersections;
     141        this.crossingWaysMap = other.crossingWaysMap;
     142        this.mpAreaCache = other.mpAreaCache;
    129143    }
    130144
     
    294308        children = null;
    295309        intersections = null;
     310        crossingWaysMap = null;
    296311    }
    297312
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java

    r15938 r15959  
    55
    66import java.awt.geom.Area;
     7import java.awt.geom.Point2D;
    78import java.text.MessageFormat;
    89import java.util.ArrayList;
     
    1213import java.util.LinkedHashSet;
    1314import java.util.List;
     15import java.util.Map;
    1416import java.util.Objects;
    1517import java.util.function.IntFunction;
     
    2628import org.openstreetmap.josm.data.osm.OsmUtils;
    2729import org.openstreetmap.josm.data.osm.Relation;
     30import org.openstreetmap.josm.data.osm.Way;
     31import org.openstreetmap.josm.data.osm.WaySegment;
    2832import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
    2933import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
     34import org.openstreetmap.josm.data.validation.tests.CrossingWays;
    3035import org.openstreetmap.josm.gui.mappaint.Environment;
    3136import org.openstreetmap.josm.gui.mappaint.Range;
     
    303308            private final String layer;
    304309            private Area area;
     310            /** Will contain all way segments, grouped by cells */
     311            Map<Point2D, List<WaySegment>> cellSegments;
    305312
    306313            private CrossingFinder(Environment e) {
    307314                super(e);
    308                 CheckParameterUtil.ensureThat(e.osm instanceof IWay, "Only ways are supported");
     315                CheckParameterUtil.ensureThat(isArea(e.osm), "Only areas are supported");
    309316                layer = OsmUtils.getLayer(e.osm);
    310317            }
    311318
    312             @Override
    313             public void visit(IWay<?> w) {
    314                 if (Objects.equals(layer, OsmUtils.getLayer(w))
    315                     && left.matches(new Environment(w).withParent(e.osm))
    316                     && e.osm instanceof IWay) {
    317                     if (area == null) {
    318                         area = Geometry.getAreaEastNorth(e.osm);
    319                     }
    320                     Pair<PolygonIntersection, Area> is = Geometry.polygonIntersectionResult(
    321                             Geometry.getAreaEastNorth(w), area, Geometry.INTERSECTION_EPS_EAST_NORTH);
    322                     if (Geometry.PolygonIntersection.CROSSING == is.a) {
    323                         addToChildren(e, w);
    324                         // store intersection area to improve highlight and zoom to problem
    325                         if (e.intersections == null) {
    326                             e.intersections = new HashMap<>();
     319            private Area getAreaEastNorth(IPrimitive p, Environment e) {
     320                if (e.mpAreaCache != null && p.isMultipolygon()) {
     321                    Area a = e.mpAreaCache.get(p);
     322                    if (a == null) {
     323                        a = Geometry.getAreaEastNorth(p);
     324                        e.mpAreaCache.put(p, a);
     325                    }
     326                    return a;
     327                }
     328                return Geometry.getAreaEastNorth(p);
     329            }
     330
     331            private Map<List<Way>, List<WaySegment>> findCrossings(IPrimitive area,
     332                    Map<Point2D, List<WaySegment>> cellSegments) {
     333                /** The detected crossing ways */
     334                Map<List<Way>, List<WaySegment>> crossingWays = new HashMap<>(50);
     335                if (area instanceof Way) {
     336                    CrossingWays.findIntersectingWay((Way) area, cellSegments, crossingWays, false);
     337                } else if (area instanceof Relation && area.isMultipolygon()) {
     338                    Relation r = (Relation) area;
     339                    for (Way w : r.getMemberPrimitives(Way.class)) {
     340                        if (!w.hasIncompleteNodes()) {
     341                            CrossingWays.findIntersectingWay(w, cellSegments, crossingWays, false);
    327342                        }
    328                         e.intersections.put(w, is.b);
     343                    }
     344                }
     345                return crossingWays;
     346            }
     347
     348            @Override
     349            public void visit(Collection<? extends IPrimitive> primitives) {
     350                List<? extends IPrimitive> toIgnore;
     351                if (e.osm instanceof Relation) {
     352                    toIgnore = ((IRelation<?>) e.osm).getMemberPrimitivesList();
     353                } else {
     354                    toIgnore = null;
     355                }
     356
     357                for (IPrimitive p : primitives) {
     358                    if (isPrimitiveUsable(p) && Objects.equals(layer, OsmUtils.getLayer(p))
     359                            && left.matches(new Environment(p).withParent(e.osm)) && isArea(p)
     360                            && (toIgnore == null || !toIgnore.contains(p))) {
     361                        if (area == null) {
     362                            area = getAreaEastNorth(e.osm, e);
     363                        }
     364                        Area otherArea = getAreaEastNorth(p, e);
     365                        if (area.isEmpty() || otherArea.isEmpty()) {
     366                            if (cellSegments == null) {
     367                                // lazy initialisation
     368                                cellSegments = new HashMap<>();
     369                                findCrossings(e.osm, cellSegments); // ignore self intersections etc. here
     370                            }
     371                            // need a copy
     372                            final Map<Point2D, List<WaySegment>> tmpCellSegments = new HashMap<>(cellSegments);
     373                            // calculate all crossings between e.osm and p
     374                            Map<List<Way>, List<WaySegment>> crossingWays = findCrossings(p, tmpCellSegments);
     375                            if (!crossingWays.isEmpty()) {
     376                                addToChildren(e, p);
     377                                if (e.crossingWaysMap == null) {
     378                                    e.crossingWaysMap = new HashMap<>();
     379                                }
     380                                e.crossingWaysMap.put(p, crossingWays);
     381                            }
     382                        } else {
     383                            // we have complete data. This allows to find intersections with shared nodes
     384                            // See #16707
     385                            Pair<PolygonIntersection, Area> is = Geometry.polygonIntersectionResult(
     386                                    otherArea, area, Geometry.INTERSECTION_EPS_EAST_NORTH);
     387                            if (Geometry.PolygonIntersection.CROSSING == is.a) {
     388                                addToChildren(e, p);
     389                                // store intersection area to improve highlight and zoom to problem
     390                                if (e.intersections == null) {
     391                                    e.intersections = new HashMap<>();
     392                                }
     393                                e.intersections.put(p, is.b);
     394                            }
     395                        }
    329396                    }
    330397                }
     
    453520                return ChildOrParentSelectorType.SUPERSET_OR_EQUAL == type ? e.children != null : e.children == null;
    454521
    455             } else if (ChildOrParentSelectorType.CROSSING == type && e.osm instanceof IWay) {
     522            } else if (ChildOrParentSelectorType.CROSSING == type) {
    456523                e.parent = e.osm;
    457                 if (right instanceof OptimizedGeneralSelector
    458                         && e.osm.getDataSet() != null
    459                         && ((OptimizedGeneralSelector) right).matchesBase(OsmPrimitiveType.WAY)) {
     524                if (e.osm.getDataSet() != null && isArea(e.osm)) {
    460525                    final CrossingFinder crossingFinder = new CrossingFinder(e);
    461                     crossingFinder.visit(e.osm.getDataSet().searchWays(e.osm.getBBox()));
     526                    visitBBox(e, crossingFinder);
     527                    return e.children != null;
    462528                }
    463529                return e.children != null;
Note: See TracChangeset for help on using the changeset viewer.