Ticket #13165: 13165_v1.patch

File 13165_v1.patch, 14.7 KB (added by GerdP, 3 years ago)
  • src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java

     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.data.validation.tests;
    33
     4import static org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker.FixCommand.evaluateObject;
    45import static org.openstreetmap.josm.tools.I18n.tr;
    56
    67import java.io.BufferedReader;
     
    575576                final String description1 = group == null ? description : group;
    576577                final String description2 = group == null ? null : description;
    577578                final List<OsmPrimitive> primitives;
     579                final Collection<OsmPrimitive> hilite;
    578580                if (env.child != null) {
    579581                    primitives = Arrays.asList(p, env.child);
    580582                } else {
    581583                    primitives = Collections.singletonList(p);
    582584                }
     585                hilite = env.hilite != null && !env.hilite.isEmpty() ? env.hilite : primitives;
     586
    583587                final TestError.Builder error = TestError.builder(tester, getSeverity(), 3000)
    584588                        .messageWithManuallyTranslatedDescription(description1, description2, matchingSelector.toString())
    585                         .primitives(primitives);
     589                        .primitives(primitives)
     590                        .highlight(hilite);
     591
    586592                if (fix != null) {
    587593                    return error.fix(() -> fix).build();
    588594                } else {
  • src/org/openstreetmap/josm/gui/mappaint/Environment.java

     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.gui.mappaint;
    33
     4import java.util.Collection;
     5
    46import org.openstreetmap.josm.data.osm.OsmPrimitive;
    57import org.openstreetmap.josm.data.osm.Relation;
    68import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.Context;
     
    2123    public StyleSource source;
    2224    private Context context = Context.PRIMITIVE;
    2325    public static final String DEFAULT_LAYER = "default";
     26    public Collection<OsmPrimitive> hilite;
    2427
    2528    /**
    2629     * If not null, this is the matching parent object if a condition or an expression
     
    9093        this.index = other.index;
    9194        this.count = other.count;
    9295        this.context = other.getContext();
     96        this.hilite = other.hilite;
    9397    }
    9498
    9599    /**
     
    243247        child = null;
    244248        index = null;
    245249        count = null;
     250        hilite = null;
    246251    }
    247252
    248253    public Cascade getCascade(String layer) {
  • src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java

     
    33
    44import static org.openstreetmap.josm.data.projection.Ellipsoid.WGS84;
    55
     6import java.awt.geom.Area;
     7import java.util.ArrayList;
    68import java.util.Collection;
    79import java.util.Collections;
     10import java.util.HashSet;
    811import java.util.List;
    912import java.util.NoSuchElementException;
     13import java.util.Set;
    1014import java.util.function.IntFunction;
    1115import java.util.function.IntSupplier;
    12 import java.util.Set;
    1316import java.util.regex.PatternSyntaxException;
    1417
    1518import org.openstreetmap.josm.Main;
     
    1619import org.openstreetmap.josm.data.osm.Node;
    1720import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1821import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     22import org.openstreetmap.josm.data.osm.PrimitiveId;
    1923import org.openstreetmap.josm.data.osm.Relation;
    2024import org.openstreetmap.josm.data.osm.RelationMember;
    2125import org.openstreetmap.josm.data.osm.Way;
    2226import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
     27import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
     28import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData;
    2329import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
    2430import org.openstreetmap.josm.gui.mappaint.Environment;
    2531import org.openstreetmap.josm.gui.mappaint.Range;
     
    2632import org.openstreetmap.josm.gui.mappaint.mapcss.ConditionFactory.OpenEndPseudoClassCondition;
    2733import org.openstreetmap.josm.tools.CheckParameterUtil;
    2834import org.openstreetmap.josm.tools.Geometry;
     35import org.openstreetmap.josm.tools.Geometry.PolygonIntersection;
    2936import org.openstreetmap.josm.tools.Pair;
    3037import org.openstreetmap.josm.tools.SubclassFilteredCollection;
    3138import org.openstreetmap.josm.tools.Utils;
     
    242249        }
    243250
    244251        private final class CrossingFinder extends AbstractFinder {
     252            HashSet<PrimitiveId> checkedPrimitives = new HashSet<>();
     253
    245254            private CrossingFinder(Environment e) {
    246255                super(e);
    247256                CheckParameterUtil.ensureThat(e.osm instanceof Way, "Only ways are supported");
    248257            }
    249258
     259            /**
     260             * Check two areas for intersection, if found, find elements to highlight
     261             * @param a1 1st area
     262             * @param a2 2nd area
     263             * @param p2 primitive that is or contains area a2
     264             */
     265            void checkIntersection(Area a1, Area a2, OsmPrimitive p2) {
     266                PolygonIntersection is = Geometry.polygonIntersection(a1, a2);
     267                if (is == PolygonIntersection.CROSSING) {
     268                    e.child = p2;
     269                }
     270            }
     271
    250272            @Override
    251273            public void visit(Way w) {
    252274                if (e.child == null && left.matches(new Environment(w).withParent(e.osm))) {
    253                     if (e.osm instanceof Way && Geometry.PolygonIntersection.CROSSING.equals(
    254                             Geometry.polygonIntersection(w.getNodes(), ((Way) e.osm).getNodes()))) {
    255                         e.child = w;
     275                    // check way-way intersection
     276                    Area a = Geometry.getArea(w.getNodes());
     277                    Area b = Geometry.getArea(((Way) e.osm).getNodes());
     278                    checkIntersection(a, b, w);
     279                    return;
     280                }
     281                if (e.parent.isMultipolygon()) {
     282                    // check mp-way intersection
     283                    if (e.child == null && left.matches(new Environment(w).withParent(e.parent))) {
     284                        if (checkedPrimitives.contains(w))
     285                            return;
     286                        checkedPrimitives.add(w);
     287                        for (OsmPrimitive p : w.getReferrers()) {
     288                            if (p == e.parent)
     289                                return;
     290                        }
     291                        Multipolygon polygon = MultipolygonCache.getInstance().get(Main.map.mapView, (Relation) e.parent);
     292                        if (polygon == null)
     293                            return;
     294                        Area a1 = Geometry.getArea(w.getNodes());
     295                        for (PolyData pd : polygon.getCombinedPolygons()) {
     296                            Area a2 = new Area(pd.get());
     297                            checkIntersection(a1, a2, w);
     298                            if (e.child != null) {
     299                                if (e.hilite == null) {
     300                                    Set<OsmPrimitive> hilite = new HashSet<>();
     301                                    hilite.add(w);
     302                                    hilite.addAll(getMpWays(e.parent, pd));
     303                                    e.hilite = new ArrayList<>(hilite);
     304                                }
     305                                return;
     306                            }
     307                        }
    256308                    }
     309                    if (e.child == null) {
     310                        // is way member of a matching mp relation ?
     311                        for (OsmPrimitive otherParent : w.getReferrers()) {
     312                            if (otherParent.isMultipolygon() && !otherParent.isIncomplete() && e.parent != otherParent) {
     313                                if (((Relation) otherParent).hasIncompleteMembers())
     314                                    continue;
     315                                if (left.matches(new Environment(otherParent).withParent(otherParent))) {
     316                                    if (checkedPrimitives.contains(otherParent))
     317                                        continue;
     318                                    checkedPrimitives.add(otherParent);
     319                                    // okay, we have two different complete multipolygons, check if they are overlapping
     320                                    Multipolygon p1 = MultipolygonCache.getInstance().get(Main.map.mapView, (Relation) e.parent);
     321                                    if (p1 == null)
     322                                        continue;
     323                                    Multipolygon p2 = MultipolygonCache.getInstance().get(Main.map.mapView, (Relation) otherParent);
     324                                    if (p2 == null)
     325                                        continue;
     326                                    for (PolyData pd1 : p1.getCombinedPolygons()) {
     327                                        Area a1 = new Area(pd1.get());
     328                                        for (PolyData pd2 : p2.getCombinedPolygons()) {
     329                                            Area a2 = new Area(pd2.get());
     330                                            checkIntersection(a1, a2, otherParent);
     331                                            if (e.child != null) {
     332                                                if (e.hilite == null) {
     333                                                    Set<OsmPrimitive> hilite = new HashSet<>();
     334                                                    hilite.addAll(getMpWays(e.parent, pd1));
     335                                                    hilite.addAll(getMpWays(otherParent, pd2));
     336                                                    e.hilite = new ArrayList<>(hilite);
     337                                                }
     338                                                return;
     339                                            }
     340                                        }
     341                                    }
     342                                }
     343                            }
     344                        }
     345                    }
     346                } else {
     347                 // check way-mp intersection
     348                    for (OsmPrimitive otherParent : w.getReferrers()) {
     349                        if (otherParent.isMultipolygon() && ((Relation) otherParent).hasIncompleteMembers() == false) {
     350                            if (left.matches(new Environment(otherParent).withParent(otherParent))) {
     351                                checkedPrimitives.add(otherParent);
     352                                if (e.parent == otherParent || ((Relation) otherParent).hasIncompleteMembers())
     353                                    return;
     354                                // we have a matching way that might cross a multipolygon, check if they are overlapping
     355                                Area a1 = Geometry.getArea(((Way) e.osm).getNodes());
     356
     357                                Multipolygon p2 = MultipolygonCache.getInstance().get(Main.map.mapView, (Relation) otherParent);
     358                                if (p2 == null)
     359                                    return;
     360                                for (PolyData pd2 : p2.getCombinedPolygons()) {
     361                                    Area a2 = new Area(pd2.get());
     362                                    checkIntersection(a1, a2, otherParent);
     363                                    if (e.child != null) {
     364                                        if (e.hilite == null) {
     365                                            Set<OsmPrimitive> hilite = new HashSet<>();
     366                                            hilite.add(e.osm);
     367                                            hilite.addAll(getMpWays(otherParent, pd2));
     368                                            e.hilite = new ArrayList<>(hilite);
     369                                        }
     370                                        return;
     371                                    }
     372                                }
     373                            }
     374                        }
     375                    }
    257376                }
    258377            }
     378
     379            private Collection<? extends OsmPrimitive> getMpWays(OsmPrimitive mpRelation, PolyData pd) {
     380                List<OsmPrimitive> ways = new ArrayList<>();
     381                for (Long id : pd.getWayIds()) {
     382                    ways.add(mpRelation.getDataSet().getPrimitiveById(id, OsmPrimitiveType.WAY));
     383                }
     384                return ways;
     385            }
    259386        }
    260387
    261388        private class ContainsFinder extends AbstractFinder {
     
    350477                    crossingFinder.visit(e.osm.getDataSet().searchWays(e.osm.getBBox()));
    351478                }
    352479                return e.child != null;
     480            } else if (ChildOrParentSelectorType.CROSSING.equals(type) && e.osm.isMultipolygon()) {
     481                Relation r = (Relation) e.osm;
     482                if (r.hasIncompleteMembers())
     483                    return false;
     484                List<RelationMember> members = r.getMembers();
     485                for (int i = 0; i < members.size(); i++) {
     486                    OsmPrimitive member = members.get(i).getMember();
     487                    if (member instanceof Way) {
     488                        Environment ew = new Environment(member);
     489                        ew.parent = e.osm;
     490                        final CrossingFinder crossingFinder = new CrossingFinder(ew);
     491                        if (right instanceof OptimizedGeneralSelector
     492                                && ((OptimizedGeneralSelector) right).matchesBase(OsmPrimitiveType.WAY)) {
     493                            List<Way> nearWays = ew.osm.getDataSet().searchWays(ew.osm.getBBox());
     494                            crossingFinder.visit(nearWays);
     495                            if (ew.child != null) {
     496                                e.child = ew.child;
     497                                e.hilite = ew.hilite;
     498                                return true;
     499                            }
     500                        }
     501                    }
     502                }
     503                return e.child != null;
    353504            } else if (ChildOrParentSelectorType.SIBLING.equals(type)) {
    354505                if (e.osm instanceof Node) {
    355506                    for (Way w : Utils.filteredCollection(e.osm.getReferrers(true), Way.class)) {