Changeset 5522 in josm for trunk


Ignore:
Timestamp:
2012-09-30T22:47:57+02:00 (12 years ago)
Author:
Don-vip
Message:

fix #7034, fix #8067 - Rework "Building inside Building" test from scratch

Location:
trunk/src/org/openstreetmap/josm
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/validation/tests/BuildingInBuilding.java

    r5300 r5522  
    55
    66import java.util.Collection;
     7import java.util.HashSet;
    78import java.util.LinkedList;
    89import java.util.List;
     10import java.util.Set;
    911
    10 import org.openstreetmap.josm.data.coor.LatLon;
    1112import org.openstreetmap.josm.data.osm.Node;
    1213import org.openstreetmap.josm.data.osm.OsmPrimitive;
     
    2122import org.openstreetmap.josm.tools.FilteredCollection;
    2223import org.openstreetmap.josm.tools.Geometry;
     24import org.openstreetmap.josm.tools.Geometry.PolygonIntersection;
    2325import org.openstreetmap.josm.tools.Predicate;
    2426
     
    4850    }
    4951
     52    @Override
     53    public void visit(Relation r) {
     54        if (r.isUsable() && r.isMultipolygon() && isBuilding(r)) {
     55            primitivesToCheck.add(r);
     56            for (RelationMember m : r.getMembers()) {
     57                if (m.getRole().equals("outer") && m.getType().equals(OsmPrimitiveType.WAY)) {
     58                    index.add(m.getWay());
     59                }
     60            }
     61        }
     62    }
     63
    5064    private static boolean isInPolygon(Node n, List<Node> polygon) {
    5165        return Geometry.nodeInsidePolygon(n, polygon);
    5266    }
    5367
    54     /**
    55      * Return true if w is in polygon.
    56      */
    57     private static boolean isInPolygon(Way w, List<Node> polygon) {
    58         // Check that all nodes of w are in polygon
    59         for (Node n : w.getNodes()) {
    60             if (!isInPolygon(n, polygon))
    61                 return false;
     68    protected class MultiPolygonMembers {
     69        public final Set<Way> outers = new HashSet<Way>();
     70        public final Set<Way> inners = new HashSet<Way>();
     71        public MultiPolygonMembers(Relation multiPolygon) {
     72            for (RelationMember m : multiPolygon.getMembers()) {
     73                if (m.getType().equals(OsmPrimitiveType.WAY)) {
     74                    if (m.getRole().equals("outer")) {
     75                        outers.add(m.getWay());
     76                    } else if (m.getRole().equals("inner")) {
     77                        inners.add(m.getWay());
     78                    }
     79                }
     80            }
    6281        }
    63         // All nodes can be inside polygon and still, w outside:
    64         //            +-------------+
    65         //           /|             |
    66         //          / |             |
    67         //         /  |             |
    68         //        / w |             |
    69         //  +----+----+             |
    70         //  |       polygon         |
    71         //  |_______________________|
    72         //
    73         for (int i=1; i<w.getNodesCount(); i++) {
    74             LatLon center = w.getNode(i).getCoor().getCenter(w.getNode(i-1).getCoor());
    75             if (center != null && !isInPolygon(new Node(center), polygon))
    76                 return false;
     82    }
     83   
     84    protected boolean sameLayers(Way w1, Way w2) {
     85        String l1 = w1.get("layer") != null ? w1.get("layer") : "0";
     86        String l2 = w2.get("layer") != null ? w2.get("layer") : "0";
     87        return l1.equals(l2);
     88    }
     89   
     90    protected boolean isWayInsideMultiPolygon(Way object, Relation multiPolygon) {
     91        // Extract outer/inner members from multipolygon
     92        MultiPolygonMembers mpm = new MultiPolygonMembers(multiPolygon);
     93        // Test if object is inside an outer member
     94        for (Way out : mpm.outers) {
     95            PolygonIntersection inter = Geometry.polygonIntersection(object.getNodes(), out.getNodes());
     96            if (inter == PolygonIntersection.FIRST_INSIDE_SECOND || inter == PolygonIntersection.CROSSING) {
     97                boolean insideInner = false;
     98                // If inside an outer, check it is not inside an inner
     99                for (Way in : mpm.inners) {
     100                    if (Geometry.polygonIntersection(in.getNodes(), out.getNodes()) == PolygonIntersection.FIRST_INSIDE_SECOND &&
     101                        Geometry.polygonIntersection(object.getNodes(), in.getNodes()) == PolygonIntersection.FIRST_INSIDE_SECOND) {
     102                        insideInner = true;
     103                        break;
     104                    }
     105                }
     106                // Inside outer but not inside inner -> the building appears to be inside a buiding
     107                if (!insideInner) {
     108                    // Final check on "layer" tag. Buildings of different layers may be superposed
     109                    if (sameLayers(object, out)) {
     110                        return true;
     111                    }
     112                }
     113            }
    77114        }
    78         return true;
     115        return false;
    79116    }
    80 
     117   
    81118    @Override
    82119    public void endTest() {
    83120        for (final OsmPrimitive p : primitivesToCheck) {
    84121            Collection<Way> outers = new FilteredCollection<Way>(index.search(p.getBBox()), new Predicate<Way>() {
     122               
     123                protected boolean evaluateNode(Node n, Way object) {
     124                    return isInPolygon(n, object.getNodes()) || object.getNodes().contains(n);
     125                }
     126               
     127                protected boolean evaluateWay(Way w, Way object) {
     128                    if (w.equals(object)) return false;
     129                   
     130                    // Get all multipolygons referencing object
     131                    Collection<OsmPrimitive> buildingMultiPolygons = new FilteredCollection<OsmPrimitive>(object.getReferrers(), new Predicate<OsmPrimitive>() {
     132                        @Override
     133                        public boolean evaluate(OsmPrimitive object) {
     134                            return primitivesToCheck.contains(object);
     135                        }
     136                    }) ;
     137                   
     138                    // if there's none, test if w is inside object
     139                    if (buildingMultiPolygons.isEmpty()) {
     140                        PolygonIntersection inter = Geometry.polygonIntersection(w.getNodes(), object.getNodes());
     141                        // Final check on "layer" tag. Buildings of different layers may be superposed
     142                        return (inter == PolygonIntersection.FIRST_INSIDE_SECOND || inter == PolygonIntersection.CROSSING) && sameLayers(w, object);
     143                    } else {
     144                        // Else, test if w is inside one of the multipolygons
     145                        for (OsmPrimitive bmp : buildingMultiPolygons) {
     146                            if (bmp instanceof Relation) {
     147                                if (isWayInsideMultiPolygon(w, (Relation) bmp)) {
     148                                    return true;
     149                                }
     150                            }
     151                        }
     152                        return false;
     153                    }
     154                }
     155               
     156                protected boolean evaluateRelation(Relation r, Way object) {
     157                    MultiPolygonMembers mpm = new MultiPolygonMembers((Relation) p);
     158                    for (Way out : mpm.outers) {
     159                        if (evaluateWay(out, object)) {
     160                            return true;
     161                        }
     162                    }
     163                    return false;
     164                }
     165               
    85166                @Override
    86167                public boolean evaluate(Way object) {
     
    88169                        return false;
    89170                    else if (p instanceof Node)
    90                         return isInPolygon((Node) p, object.getNodes()) || object.getNodes().contains(p);
     171                        return evaluateNode((Node) p, object);
    91172                    else if (p instanceof Way)
    92                         return isInPolygon((Way) p, object.getNodes()) && !isInInnerWay((Way)p, object);
    93                     else
    94                         return false;
     173                        return evaluateWay((Way) p, object);
     174                    else if (p instanceof Relation)
     175                        return evaluateRelation((Relation) p, object);
     176                    return false;
    95177                }
    96178            });
     179           
    97180            if (!outers.isEmpty()) {
    98181                errors.add(new TestError(this, Severity.WARNING,
     
    103186        super.endTest();
    104187    }
    105 
    106     private boolean isInInnerWay(Way w, Way outer) {
    107         for (OsmPrimitive r : outer.getReferrers()) {
    108             if (r instanceof Relation && ((Relation)r).isMultipolygon()) {
    109                 for (RelationMember m : ((Relation)r).getMembers()) {
    110                     if (m.hasRole() && m.getRole().equals("inner") && m.getType().equals(OsmPrimitiveType.WAY)) {
    111                         // Only check inner ways actually inside the current outer
    112                         Way inner = m.getWay();
    113                         if (isInPolygon(inner, outer.getNodes())) {
    114                             // If the tested way is inside this inner, outer is a false positive
    115                             if (isInPolygon(w, inner.getNodes()))
    116                                 return true;
    117                         }
    118                     }
    119                 }
    120             }
    121         }
    122         return false;
    123     }
    124188}
  • trunk/src/org/openstreetmap/josm/tools/Geometry.java

    r5313 r5522  
    22package org.openstreetmap.josm.tools;
    33
     4import java.awt.Rectangle;
     5import java.awt.geom.Area;
    46import java.awt.geom.Line2D;
     7import java.awt.geom.Path2D;
    58import java.math.BigDecimal;
    69import java.math.MathContext;
    710import java.util.ArrayList;
    811import java.util.Comparator;
    9 import java.util.HashSet;
    1012import java.util.LinkedHashSet;
    1113import java.util.List;
     
    369371    }
    370372
     373    private static Area getArea(List<Node> polygon) {
     374        Path2D path = new Path2D.Double();
     375
     376        boolean begin = true;
     377        for (Node n : polygon) {
     378            if (begin) {
     379                path.moveTo(n.getEastNorth().getX(), n.getEastNorth().getY());
     380                begin = false;
     381            } else {
     382                path.lineTo(n.getEastNorth().getX(), n.getEastNorth().getY());
     383            }
     384        }
     385        path.closePath();
     386       
     387        return new Area(path);
     388    }
     389   
    371390    /**
    372391     * Tests if two polygons intersect.
     
    374393     * @param second
    375394     * @return intersection kind
    376      * TODO: test segments, not only points
    377      * TODO: is O(N*M), should use sweep for better performance.
    378395     */
    379396    public static PolygonIntersection polygonIntersection(List<Node> first, List<Node> second) {
    380         Set<Node> firstSet = new HashSet<Node>(first);
    381         Set<Node> secondSet = new HashSet<Node>(second);
    382 
    383         int nodesInsideSecond = 0;
    384         int nodesOutsideSecond = 0;
    385         int nodesInsideFirst = 0;
    386         int nodesOutsideFirst = 0;
    387 
    388         for (Node insideNode : first) {
    389             if (secondSet.contains(insideNode)) {
    390                 continue;
    391                 //ignore touching nodes.
    392             }
    393 
    394             if (nodeInsidePolygon(insideNode, second)) {
    395                 nodesInsideSecond ++;
    396             }
    397             else {
    398                 nodesOutsideSecond ++;
    399             }
    400         }
    401 
    402         for (Node insideNode : second) {
    403             if (firstSet.contains(insideNode)) {
    404                 continue;
    405                 //ignore touching nodes.
    406             }
    407 
    408             if (nodeInsidePolygon(insideNode, first)) {
    409                 nodesInsideFirst ++;
    410             }
    411             else {
    412                 nodesOutsideFirst ++;
    413             }
    414         }
    415 
    416         if (nodesInsideFirst == 0) {
    417             if (nodesInsideSecond == 0){
    418                 if (nodesOutsideFirst + nodesInsideSecond > 0)
    419                     return PolygonIntersection.OUTSIDE;
    420                 else
    421                     //all nodes common
    422                     return PolygonIntersection.CROSSING;
    423             } else
    424                 return PolygonIntersection.FIRST_INSIDE_SECOND;
    425         }
    426         else
    427         {
    428             if (nodesInsideSecond == 0)
    429                 return PolygonIntersection.SECOND_INSIDE_FIRST;
    430             else
    431                 return PolygonIntersection.CROSSING;
     397       
     398        Area a1 = getArea(first);
     399        Area a2 = getArea(second);
     400       
     401        Area inter = new Area(a1);
     402        inter.intersect(a2);
     403       
     404        Rectangle bounds = inter.getBounds();
     405       
     406        if (inter.isEmpty() || bounds.getHeight()*bounds.getWidth() <= 1.0) {
     407            return PolygonIntersection.OUTSIDE;
     408        } else if (inter.equals(a1)) {
     409            return PolygonIntersection.FIRST_INSIDE_SECOND;
     410        } else if (inter.equals(a2)) {
     411            return PolygonIntersection.SECOND_INSIDE_FIRST;
     412        } else {
     413            return PolygonIntersection.CROSSING;
    432414        }
    433415    }
Note: See TracChangeset for help on using the changeset viewer.