Changeset 15102 in josm


Ignore:
Timestamp:
2019-05-21T10:04:36+02:00 (5 months ago)
Author:
GerdP
Message:

fix #10391: Add support for "not element of" operator

  • implement 4 new operators: ⊆,⊈,⊇,⊉
  • ⊆ is a synonym for the existing ∈ operator that uses the ContainsFinder, it

matches for elements which contain one or more elements matching the left Selectors

  • ⊈ matches for elements which do not contain any element matching the left Selectors (also uses the ContainsFinder)
  • ⊇ matches for elements which are contained in an element matching the left Selectors, it uses the InsideOrEqualFinder and is typically slower than ⊆, so it is probably only usefull for the search dialog
  • ⊉ matches for elements which are NOT contained in any element matching the left Selector
  • Both ContainsFinder and InsideOrEqualFinder work with areas, an area is either descibed by a closed way or a valid, complete relation of type=multipolygon or type=boundary. Incomplete objects do not contain something, invalid objects produce unpredictable results.
  • An element A contains another element B when it is an area and when B is either a Node inside the area of A, or when it is also an area that is fully inside or equal to A. An element is not inside a relation when it is a member of that relation.
  • hint: with ∈,⊆,⊈ prefer to use area selector instead of * for the right side, for ⊇,⊉ use area selector on the left side
Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/data_nodist/amenity-in-amenity.osm

    r11887 r15102  
    55  <node id='-3875' action='modify' visible='true' lat='-22.6907376271' lon='-48.56085907164' />
    66  <node id='-3871' action='modify' visible='true' lat='-22.69070289438' lon='-48.56081834975' />
    7   <node id='-3864' visible='true' lat='-22.69073023197' lon='-48.56082133585'>
    8     <tag k='amenity' v='hospital' />
    9   </node>
    107  <node id='-3863' visible='true' lat='-22.69071631095' lon='-48.56081028236' />
    118  <node id='-3862' visible='true' lat='-22.69074204865' lon='-48.56081115962' />
    129  <node id='-3861' visible='true' lat='-22.69071809155' lon='-48.56082765213' />
    1310  <node id='-3860' visible='true' lat='-22.69074528609' lon='-48.56083782836' />
     11  <node id='123' version='3' lat='-22.69073023197' lon='-48.56082133585'>
     12    <tag k='amenity' v='hospital' />
     13  </node>
    1414  <way id='-3880' action='modify' visible='true'>
    1515    <nd ref='-3877' />
  • trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java

    r15064 r15102  
    867867            if (partialSelection && r.selector instanceof Selector.ChildOrParentSelector) {
    868868                ChildOrParentSelector sel = (Selector.ChildOrParentSelector) r.selector;
    869                 if (sel.type == ChildOrParentSelectorType.ELEMENT_OF && p.getDataSet() != null) {
     869                boolean needEnclosing = sel.type == ChildOrParentSelectorType.SUBSET_OR_EQUAL
     870                        || sel.type == ChildOrParentSelectorType.NOT_SUBSET_OR_EQUAL;
     871                if (needEnclosing && p.getDataSet() != null) {
    870872                    List<OsmPrimitive> toCheck = new ArrayList<>();
    871873                    toCheck.addAll(p.getDataSet().searchWays(p.getBBox()));
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj

    r14746 r15102  
    217217|   < FULLSTOP: "." >
    218218|   < DEG: "°" >
    219 |   < ELEMENT_OF: "∈" >
     219|   < SUBSET_OR_EQUAL: ["∈","⊆"] >
     220|   < NOT_SUBSET_OR_EQUAL: "⊈" >
     221|   < SUPERSET_OR_EQUAL: "⊇" >
     222|   < NOT_SUPERSET_OR_EQUAL: "⊉" >
    220223|   < CROSSING: "⧉" >
    221224|   < PERCENT: "%" >
     
    691694                ( ( c=condition(Context.LINK) | c=class_or_pseudoclass(Context.LINK) ) { conditions.add(c); } )*
    692695            |
    693                 <ELEMENT_OF> { type = Selector.ChildOrParentSelectorType.ELEMENT_OF; }
     696                <SUBSET_OR_EQUAL> { type = Selector.ChildOrParentSelectorType.SUBSET_OR_EQUAL; }
     697            |
     698                <NOT_SUBSET_OR_EQUAL> { type = Selector.ChildOrParentSelectorType.NOT_SUBSET_OR_EQUAL; }
     699            |
     700                <SUPERSET_OR_EQUAL> { type = Selector.ChildOrParentSelectorType.SUPERSET_OR_EQUAL; }
     701            |
     702                <NOT_SUPERSET_OR_EQUAL> { type = Selector.ChildOrParentSelectorType.NOT_SUPERSET_OR_EQUAL; }
    694703            |
    695704                <CROSSING> { type = Selector.ChildOrParentSelectorType.CROSSING; }
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java

    r15083 r15102  
    118118     */
    119119    enum ChildOrParentSelectorType {
    120         CHILD, PARENT, ELEMENT_OF, CROSSING, SIBLING
     120        CHILD, PARENT, SUBSET_OR_EQUAL, NOT_SUBSET_OR_EQUAL, SUPERSET_OR_EQUAL, NOT_SUPERSET_OR_EQUAL, CROSSING, SIBLING,
    121121    }
    122122
     
    313313        }
    314314
     315        /**
     316         * Finds elements which are inside the right element, collects those in {@code children}
     317         */
    315318        private class ContainsFinder extends AbstractFinder {
    316319            protected List<IPrimitive> toCheck;
     
    349352        }
    350353
     354        /**
     355         * Finds elements which are inside the left element, or in other words, it finds elements enclosing e.osm.
     356         * The found enclosing elements are collected in {@code e.children}.
     357         */
     358        private class InsideOrEqualFinder extends AbstractFinder {
     359
     360            protected InsideOrEqualFinder(Environment e) {
     361                super(e);
     362            }
     363
     364            @Override
     365            public void visit(IWay<?> w) {
     366                if (left.matches(new Environment(w).withParent(e.osm))
     367                        && w.getBBox().bounds(e.osm.getBBox())
     368                        && !Geometry.filterInsidePolygon(Collections.singletonList(e.osm), w).isEmpty()) {
     369                    addToChildren(e, w);
     370                }
     371            }
     372
     373            @Override
     374            public void visit(IRelation<?> r) {
     375                if (r instanceof Relation && r.isMultipolygon() && r.getBBox().bounds(e.osm.getBBox())
     376                        && left.matches(new Environment(r).withParent(e.osm))
     377                        && !Geometry.filterInsideMultipolygon(Collections.singletonList(e.osm), (Relation) r).isEmpty()) {
     378                    addToChildren(e, r);
     379                }
     380            }
     381        }
     382
     383        private void visitBBox(Environment e, AbstractFinder finder) {
     384            boolean withNodes = finder instanceof ContainsFinder;
     385            if (left instanceof OptimizedGeneralSelector) {
     386                if (withNodes && ((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.NODE)) {
     387                    finder.visit(e.osm.getDataSet().searchNodes(e.osm.getBBox()));
     388                }
     389                if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.WAY)) {
     390                    finder.visit(e.osm.getDataSet().searchWays(e.osm.getBBox()));
     391                }
     392                if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.RELATION)) {
     393                    finder.visit(e.osm.getDataSet().searchRelations(e.osm.getBBox()));
     394                }
     395            } else {
     396                if (withNodes) {
     397                    finder.visit(e.osm.getDataSet().searchNodes(e.osm.getBBox()));
     398                }
     399                finder.visit(e.osm.getDataSet().searchWays(e.osm.getBBox()));
     400                finder.visit(e.osm.getDataSet().searchRelations(e.osm.getBBox()));
     401            }
     402        }
     403
     404        private static boolean isArea(IPrimitive p) {
     405            return (p instanceof IWay && ((IWay<?>) p).isClosed() && ((IWay<?>) p).getNodesCount() >= 4)
     406                    || (p instanceof IRelation && p.isMultipolygon() && !p.isIncomplete());
     407        }
     408
    351409        @Override
    352410        public boolean matches(Environment e) {
     
    355413                return false;
    356414
    357             if (ChildOrParentSelectorType.ELEMENT_OF == type) {
    358 
    359                 if (e.osm instanceof INode || e.osm.getDataSet() == null) {
    360                     // nodes cannot contain elements
    361                     return false;
    362                 }
    363 
     415            if (ChildOrParentSelectorType.SUBSET_OR_EQUAL == type || ChildOrParentSelectorType.NOT_SUBSET_OR_EQUAL == type) {
     416
     417                if (e.osm.getDataSet() == null || !isArea(e.osm)) {
     418                    // only areas can contain elements
     419                    return ChildOrParentSelectorType.NOT_SUBSET_OR_EQUAL == type;
     420                }
    364421                ContainsFinder containsFinder = new ContainsFinder(e);
    365422                e.parent = e.osm;
    366423
    367                 if (left instanceof OptimizedGeneralSelector) {
    368                     if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.NODE)) {
    369                         containsFinder.visit(e.osm.getDataSet().searchNodes(e.osm.getBBox()));
    370                     }
    371                     if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.WAY)) {
    372                         containsFinder.visit(e.osm.getDataSet().searchWays(e.osm.getBBox()));
    373                     }
    374                     if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.RELATION)) {
    375                         containsFinder.visit(e.osm.getDataSet().searchRelations(e.osm.getBBox()));
    376                     }
    377                 } else {
    378                     // use slow test
    379                     containsFinder.visit(e.osm.getDataSet().allPrimitives());
    380                 }
     424                visitBBox(e, containsFinder);
    381425                containsFinder.execGeometryTests();
    382                 return e.children != null;
     426                return ChildOrParentSelectorType.SUBSET_OR_EQUAL == type ? e.children != null : e.children == null;
     427
     428            } else if (ChildOrParentSelectorType.SUPERSET_OR_EQUAL == type || ChildOrParentSelectorType.NOT_SUPERSET_OR_EQUAL == type) {
     429
     430                if (e.osm.getDataSet() == null || (e.osm instanceof INode && ((INode) e.osm).getCoor() == null)
     431                        || (!(e.osm instanceof INode) && !isArea(e.osm))) {
     432                    return ChildOrParentSelectorType.NOT_SUPERSET_OR_EQUAL == type;
     433                }
     434
     435                InsideOrEqualFinder insideOrEqualFinder = new InsideOrEqualFinder(e);
     436                e.parent = e.osm;
     437
     438                visitBBox(e, insideOrEqualFinder);
     439                return ChildOrParentSelectorType.SUPERSET_OR_EQUAL == type ? e.children != null : e.children == null;
    383440
    384441            } else if (ChildOrParentSelectorType.CROSSING == type && e.osm instanceof IWay) {
  • trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ChildOrParentSelectorTest.java

    r15034 r15102  
    187187    }
    188188
     189    /**
     190     * Test inside/contains selectors (spatial test)
     191     */
    189192    @Test
    190193    public void testContains() throws Exception {
    191194        ds = OsmReader.parseDataSet(Files.newInputStream(Paths.get("data_nodist/amenity-in-amenity.osm")), null);
    192195        ChildOrParentSelector css = parse("node[tag(\"amenity\") = parent_tag(\"amenity\")] ∈ *[amenity] {}");
     196        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.NODE))));
    193197        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.WAY))));
    194198        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.RELATION))));
     199        css = parse("node[tag(\"amenity\") = parent_tag(\"amenity\")] ⊆  *[amenity] {}");
     200        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.NODE))));
     201        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.WAY))));
     202        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.RELATION))));
     203        css = parse("node[tag(\"amenity\") = parent_tag(\"amenity\")] ⊈  *[amenity] {}");
     204        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.NODE))));
     205        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.WAY))));
     206        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.RELATION))));
     207        css = parse("*[tag(\"amenity\") = parent_tag(\"amenity\")] ⊇  *[amenity] {}");
     208        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.NODE))));
     209        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.WAY))));
     210        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.RELATION))));
     211        css = parse("*[tag(\"amenity\") = parent_tag(\"amenity\")] ⊉  *[amenity] {}");
     212        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.NODE))));
     213        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.WAY))));
     214        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.RELATION))));
    195215    }
    196216}
Note: See TracChangeset for help on using the changeset viewer.