Ignore:
Timestamp:
2014-01-03T11:12:16+01:00 (10 years ago)
Author:
simon04
Message:

see #9516 - MapCSS: add basic support for a "contains" expression

The current syntax is outer ∋ inner, e.g., way[building?] ∋ node[building?].

This implementation is known to be inefficient since, for every match on the right side, it checks all primitives of the dataset for a match.

Nevertheless, this implementation allows mappers to write specific MapCSS-based tests involving this spatial operator.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/tools/Geometry.java

    r6566 r6607  
    99import java.math.MathContext;
    1010import java.util.ArrayList;
     11import java.util.Collections;
    1112import java.util.Comparator;
     13import java.util.EnumSet;
     14import java.util.HashSet;
    1215import java.util.LinkedHashSet;
    1316import java.util.List;
     
    2326import org.openstreetmap.josm.data.osm.Node;
    2427import org.openstreetmap.josm.data.osm.NodePositionComparator;
     28import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     29import org.openstreetmap.josm.data.osm.Relation;
     30import org.openstreetmap.josm.data.osm.RelationMember;
    2531import org.openstreetmap.josm.data.osm.Way;
    2632
     
    736742        }
    737743    }
     744
     745    public static class MultiPolygonMembers {
     746        public final Set<Way> outers = new HashSet<Way>();
     747        public final Set<Way> inners = new HashSet<Way>();
     748
     749        public MultiPolygonMembers(Relation multiPolygon) {
     750            for (RelationMember m : multiPolygon.getMembers()) {
     751                if (m.getType().equals(OsmPrimitiveType.WAY)) {
     752                    if (m.getRole().equals("outer")) {
     753                        outers.add(m.getWay());
     754                    } else if (m.getRole().equals("inner")) {
     755                        inners.add(m.getWay());
     756                    }
     757                }
     758            }
     759        }
     760    }
     761
     762    /**
     763     * Tests if the {@code node} is inside the multipolygon {@code multiPolygon}. The nullable argument
     764     * {@code isOuterWayAMatch} allows to decide if the immediate {@code outer} way of the multipolygon is a match.
     765     */
     766    public static boolean isNodeInsideMultiPolygon(Node node, Relation multiPolygon, Predicate<Way> isOuterWayAMatch) {
     767        return isPolygonInsideMultiPolygon(Collections.singletonList(node), multiPolygon, isOuterWayAMatch);
     768    }
     769
     770    /**
     771     * Tests if the polygon formed by {@code nodes} is inside the multipolygon {@code multiPolygon}. The nullable argument
     772     * {@code isOuterWayAMatch} allows to decide if the immediate {@code outer} way of the multipolygon is a match.
     773     * <p/>
     774     * If {@code nodes} contains exactly one element, then it is checked whether that one node is inside the multipolygon.
     775     */
     776    public static boolean isPolygonInsideMultiPolygon(List<Node> nodes, Relation multiPolygon, Predicate<Way> isOuterWayAMatch) {
     777        // Extract outer/inner members from multipolygon
     778        MultiPolygonMembers mpm = new MultiPolygonMembers(multiPolygon);
     779        // Test if object is inside an outer member
     780        for (Way out : mpm.outers) {
     781            if (nodes.size() == 1
     782                    ? nodeInsidePolygon(nodes.get(0), out.getNodes())
     783                    : EnumSet.of(PolygonIntersection.FIRST_INSIDE_SECOND, PolygonIntersection.CROSSING).contains(polygonIntersection(nodes, out.getNodes()))) {
     784                boolean insideInner = false;
     785                // If inside an outer, check it is not inside an inner
     786                for (Way in : mpm.inners) {
     787                    if (polygonIntersection(in.getNodes(), out.getNodes()) == PolygonIntersection.FIRST_INSIDE_SECOND
     788                            && (nodes.size() == 1
     789                            ? nodeInsidePolygon(nodes.get(0), in.getNodes())
     790                            : polygonIntersection(nodes, in.getNodes()) == PolygonIntersection.FIRST_INSIDE_SECOND)) {
     791                        insideInner = true;
     792                        break;
     793                    }
     794                }
     795                // Inside outer but not inside inner -> the polygon appears to be inside a the multipolygon
     796                if (!insideInner) {
     797                    // Final check using predicate
     798                    if (isOuterWayAMatch == null || isOuterWayAMatch.evaluate(out)) {
     799                        return true;
     800                    }
     801                }
     802            }
     803        }
     804        return false;
     805    }
    738806}
Note: See TracChangeset for help on using the changeset viewer.