Ignore:
Timestamp:
2009-10-30T23:53:08+01:00 (15 years ago)
Author:
dhansen
Message:

This uses the new quadbuckets code for the UnconnectedWays test,
as well as a few other optimizations.

Using a large (~18MB) data set, the test took 187.827 seconds in
one instance. After applying this patch, the same test takes
1.631 seconds.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnconnectedWays.java

    r18223 r18379  
    55import java.awt.geom.Area;
    66import java.awt.geom.Line2D;
     7import java.awt.geom.Point2D;
     8import java.util.ArrayList;
    79import java.util.Arrays;
     10import java.util.Collection;
     11import java.util.Collections;
     12import java.util.List;
    813import java.util.HashMap;
    914import java.util.HashSet;
     
    1217
    1318import org.openstreetmap.josm.Main;
     19import org.openstreetmap.josm.data.coor.LatLon;
     20import org.openstreetmap.josm.data.coor.EastNorth;
    1421import org.openstreetmap.josm.data.osm.Node;
    1522import org.openstreetmap.josm.data.osm.OsmUtils;
    1623import org.openstreetmap.josm.data.osm.Way;
    1724import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     25import org.openstreetmap.josm.data.osm.DataSet;
     26import org.openstreetmap.josm.data.osm.QuadBuckets;
    1827import org.openstreetmap.josm.plugins.validator.PreferenceEditor;
    1928import org.openstreetmap.josm.plugins.validator.Severity;
     
    3645    Set<Node> middlenodes; // nodes in middle of way
    3746    Set<Node> othernodes; // nodes appearing at least twice
     47    //NodeSearchCache nodecache;
     48    QuadBuckets<Node> nodecache;
     49    Area ds_area;
     50    DataSet ds;
    3851
    3952    double mindist;
     
    5972        mindist = Main.pref.getDouble(PREFIX + ".node_way_distance", 10.0)/6378135.0;
    6073        minmiddledist = Main.pref.getDouble(PREFIX + ".way_way_distance", 0.0)/6378135.0;
     74        this.ds = Main.main.getCurrentDataSet();
     75        this.ds_area = ds.getDataSourceArea();
    6176    }
    6277
     
    6479    public void endTest()
    6580    {
    66         Area a = Main.main.getCurrentDataSet().getDataSourceArea();
     81        //Area a = Main.ds.getDataSourceArea();
    6782        Map<Node, Way> map = new HashMap<Node, Way>();
    68         for(Node en : endnodes_highway)
    69         {
    70             Boolean isexit = OsmUtils.getOsmBoolean(en.get("noexit"));
    71             if("turning_circle".equals(en.get("highway")) ||
    72             (isexit != null && isexit) || en.get("barrier") != null)
    73                 continue;
    74             for(MyWaySegment s : ways)
    75             {
    76                 if(!s.isBoundary && !s.isAbandoned && s.highway && s.nearby(en, mindist) && (a == null || a.contains(en.getCoor())))
     83        long last = -1;
     84        for (int iter = 0; iter < 1; iter++) {
     85        last = System.currentTimeMillis();
     86        long last_print = -1;
     87        int nr = 0;
     88        Collection<MyWaySegment> tmp_ways = ways;
     89        for(MyWaySegment s : tmp_ways) {
     90            nr++;
     91            long now = System.currentTimeMillis();
     92            if (now - last_print > 200) {
     93                System.err.println("processing segment nr: " + nr + " of " + ways.size());
     94                last_print = now;
     95            }
     96            for(Node en : s.nearbyNodes(mindist)) {
     97                if (en == null)
     98                    continue;
     99                //if("turning_circle".equals(en.get("highway")) ||
     100                //        (isexit != null && isexit) || en.get("barrier") != null)
     101                //    c4++;
     102                if(!s.highway)
     103                    continue;
     104                if (!endnodes_highway.contains(en))
     105                    continue;
     106                Boolean isexit = OsmUtils.getOsmBoolean(en.get("noexit"));
     107                if("turning_circle".equals(en.get("highway")) ||
     108                  (isexit != null && isexit) || en.get("barrier") != null)
     109                    continue;
     110                // There's a small false-positive here.  Imagine an intersection
     111                // like a 't'.  If the top part of the 't' is short enough, it
     112                // will trigger the node at the very top of the 't' to be unconnected
     113                // to the way that "crosses" the 't'.  We should probably check that
     114                // the ways to which 'en' belongs are not connected to 's.w'.
     115                map.put(en, s.w);
     116            }
     117        }
     118        System.out.println("p1 elapesd: " + (System.currentTimeMillis()-last));
     119        last = System.currentTimeMillis();
     120        }
     121        for(Map.Entry<Node, Way> error : map.entrySet())
     122        {
     123            errors.add(new TestError(this, Severity.WARNING,
     124            tr("Way end node near other highway"), UNCONNECTED_WAYS,
     125            Arrays.asList(error.getKey(), error.getValue())));
     126        }
     127        map.clear();
     128        for(MyWaySegment s : ways)
     129        {
     130            for(Node en : s.nearbyNodes(mindist))
     131            {
     132                if (endnodes_highway.contains(en) && !s.highway && !s.isArea()) {
    77133                    map.put(en, s.w);
    78             }
    79         }
    80         if(map.size() > 0)
    81         {
    82             for(Map.Entry<Node, Way> error : map.entrySet())
    83             {
    84                 errors.add(new TestError(this, Severity.WARNING,
    85                 tr("Way end node near other highway"), UNCONNECTED_WAYS,
    86                 Arrays.asList(error.getKey(), error.getValue())));
    87             }
    88         }
    89         map.clear();
    90         for(Node en : endnodes_highway)
    91         {
    92             for(MyWaySegment s : ways)
    93             {
    94                 if(!s.isBoundary && !s.isAbandoned && !s.highway && s.nearby(en, mindist) && !s.isArea() && (a == null || a.contains(en.getCoor())))
     134                } else if (endnodes.contains(en) && !s.isArea()) {
    95135                    map.put(en, s.w);
    96             }
    97         }
    98         for(Node en : endnodes)
    99         {
    100             for(MyWaySegment s : ways)
    101             {
    102                 if(!s.isBoundary && !s.isAbandoned && s.nearby(en, mindist) && !s.isArea() && (a == null || a.contains(en.getCoor())))
    103                     map.put(en, s.w);
    104             }
    105         }
    106         if(map.size() > 0)
    107         {
    108             for(Map.Entry<Node, Way> error : map.entrySet())
    109             {
    110                 errors.add(new TestError(this, Severity.WARNING,
    111                 tr("Way end node near other way"), UNCONNECTED_WAYS,
    112                 Arrays.asList(error.getKey(), error.getValue())));
    113             }
     136                }
     137            }
     138        }
     139        System.out.println("p2 elapesd: " + (System.currentTimeMillis()-last));
     140        last = System.currentTimeMillis();
     141        for(Map.Entry<Node, Way> error : map.entrySet())
     142        {
     143            errors.add(new TestError(this, Severity.WARNING,
     144            tr("Way end node near other way"), UNCONNECTED_WAYS,
     145            Arrays.asList(error.getKey(), error.getValue())));
    114146        }
    115147        /* the following two use a shorter distance */
     
    117149        {
    118150            map.clear();
    119             for(Node en : middlenodes)
    120             {
    121                 for(MyWaySegment s : ways)
     151            for(MyWaySegment s : ways)
     152            {
     153                for(Node en : s.nearbyNodes(minmiddledist))
    122154                {
    123                     if(!s.isBoundary && !s.isAbandoned && s.nearby(en, minmiddledist) && (a == null || a.contains(en.getCoor())))
    124                         map.put(en, s.w);
     155                    if (!middlenodes.contains(en))
     156                        continue;
     157                    map.put(en, s.w);
    125158                }
    126159            }
    127             if(map.size() > 0)
    128             {
    129                 for(Map.Entry<Node, Way> error : map.entrySet())
     160            System.out.println("p3 elapesd: " + (System.currentTimeMillis()-last));
     161            last = System.currentTimeMillis();
     162            for(Map.Entry<Node, Way> error : map.entrySet())
     163            {
     164                errors.add(new TestError(this, Severity.OTHER,
     165                tr("Way node near other way"), UNCONNECTED_WAYS,
     166                Arrays.asList(error.getKey(), error.getValue())));
     167            }
     168            map.clear();
     169            for(MyWaySegment s : ways)
     170            {
     171                for(Node en : s.nearbyNodes(minmiddledist))
    130172                {
    131                     errors.add(new TestError(this, Severity.OTHER,
    132                     tr("Way node near other way"), UNCONNECTED_WAYS,
    133                     Arrays.asList(error.getKey(), error.getValue())));
     173                    if (!othernodes.contains(en))
     174                        continue;
     175                    map.put(en, s.w);
    134176                }
    135177            }
    136             map.clear();
    137             for(Node en : othernodes)
    138             {
    139                 for(MyWaySegment s : ways)
    140                 {
    141                     if(!s.isBoundary && !s.isAbandoned && s.nearby(en, minmiddledist) && (a == null || a.contains(en.getCoor())))
    142                         map.put(en, s.w);
    143                 }
    144             }
    145             if(map.size() > 0)
    146             {
    147                 for(Map.Entry<Node, Way> error : map.entrySet())
    148                 {
    149                     errors.add(new TestError(this, Severity.OTHER,
    150                     tr("Connected way end node near other way"), UNCONNECTED_WAYS,
    151                     Arrays.asList(error.getKey(), error.getValue())));
    152                 }
     178            System.out.println("p4 elapesd: " + (System.currentTimeMillis()-last));
     179            last = System.currentTimeMillis();
     180            for(Map.Entry<Node, Way> error : map.entrySet())
     181            {
     182                errors.add(new TestError(this, Severity.OTHER,
     183                tr("Connected way end node near other way"), UNCONNECTED_WAYS,
     184                Arrays.asList(error.getKey(), error.getValue())));
    153185            }
    154186        }
     
    156188        endnodes = null;
    157189        super.endTest();
     190        System.out.println("p99 elapesd: " + (System.currentTimeMillis()-last));
     191        last = System.currentTimeMillis();
    158192    }
    159193
     
    165199        public boolean isBoundary = false;
    166200        public boolean highway;
     201        private double len;
     202        private Set<Node> nearbyNodeCache;
     203        double nearbyNodeCacheDist = -1.0;
     204        Node n1;
     205        Node n2;
    167206
    168207        public MyWaySegment(Way w, Node n1, Node n2)
     
    175214            this.isBoundary = !this.highway && "administrative".equals(w.get("boundary"));
    176215            line = new Line2D.Double(n1.getEastNorth().east(), n1.getEastNorth().north(),
    177             n2.getEastNorth().east(), n2.getEastNorth().north());
     216                                     n2.getEastNorth().east(), n2.getEastNorth().north());
     217            len = line.getP1().distance(line.getP2());
     218            this.n1 = n1;
     219            this.n2 = n2;
    178220        }
    179221
    180222        public boolean nearby(Node n, double dist)
    181223        {
    182             return !w.containsNode(n)
    183             && line.ptSegDist(n.getEastNorth().east(), n.getEastNorth().north()) < dist;
     224//            return !w.containsNode(n)
     225//            && line.ptSegDist(n.getEastNorth().east(), n.getEastNorth().north()) < dist;
     226            if (w == null)
     227                Main.debug("way null");
     228            if (w.containsNode(n))
     229                return false;
     230            EastNorth coord = n.getEastNorth();
     231            if (coord == null)
     232                return false;
     233            Point2D p = new Point2D.Double(coord.east(), coord.north());
     234            if (line.getP1().distance(p) > len+dist)
     235                return false;
     236            if (line.getP2().distance(p) > len+dist)
     237                return false;
     238            return line.ptSegDist(p) < dist;
     239        }
     240        public List<LatLon> getBounds(double fudge)
     241        {
     242            double x1 = n1.getCoor().lon();
     243            double x2 = n2.getCoor().lon();
     244            if (x1 > x2) {
     245                double tmpx = x1;
     246                x1 = x2;
     247                x2 = tmpx;
     248            }
     249            double y1 = n1.getCoor().lat();
     250            double y2 = n2.getCoor().lat();
     251            if (y1 > y2) {
     252                double tmpy = y1;
     253                y1 = y2;
     254                y2 = tmpy;
     255            }
     256            LatLon topLeft  = new LatLon(y2+fudge, x1-fudge);
     257            LatLon botRight = new LatLon(y1-fudge, x2+fudge);
     258            List<LatLon> ret = new ArrayList<LatLon>();
     259            ret.add(topLeft);
     260            ret.add(botRight);
     261            return ret;
     262        }
     263
     264        public Collection<Node> nearbyNodes(double dist)
     265        {
     266            // If you're looking for nodes that are farther
     267            // away that we looked for last time, the cached
     268            // result is no good
     269            if (dist > nearbyNodeCacheDist) {
     270                //if (nearbyNodeCacheDist != -1)
     271                //    System.out.println("destroyed MyWaySegment nearby node cache:" + dist + " > " +  nearbyNodeCacheDist);
     272                nearbyNodeCache = null;
     273            }
     274            if (nearbyNodeCache != null) {
     275                // If we've cached an aread greater than the
     276                // one now being asked for...
     277                if (nearbyNodeCacheDist > dist) {
     278                    System.out.println("had to trim MyWaySegment nearby node cache.");
     279                    // Used the cached result and trim out
     280                    // the nodes that are not in the smaller
     281                    // area, but keep the old larger cache.
     282                    Set<Node> trimmed = new HashSet<Node>(nearbyNodeCache);
     283                    for (Node n : new HashSet<Node>(nearbyNodeCache)) {
     284                        if (!nearby(n, dist))
     285                            trimmed.remove(n);
     286                    }
     287                    return trimmed;
     288                }
     289                return nearbyNodeCache;
     290            }
     291            /*
     292             * We know that any point near the line must be at
     293             * least as close as the other end of the line, plus
     294             * a little fudge for the distance away ('dist').
     295             */
     296
     297            // This needs to be a hash set because the searches
     298            // overlap a bit and can return duplicate nodes.
     299            nearbyNodeCache = null;
     300            nodecache = ds.nodes;
     301            List<LatLon> bounds = this.getBounds(dist);
     302            List<Node> found_nodes = nodecache.search(bounds.get(0), bounds.get(1));
     303            if (found_nodes == null)
     304                return Collections.emptySet();
     305
     306            for (Node n : found_nodes) {
     307                if (!nearby(n, dist) ||
     308                     (ds_area != null && !ds_area.contains(n.getCoor())))
     309                    continue;
     310                // It is actually very rare for us to find a node
     311                // so defer as much of the work as possible, like
     312                // allocating the hash set
     313                if (nearbyNodeCache == null)
     314                    nearbyNodeCache = new HashSet<Node>();
     315                nearbyNodeCache.add(n);
     316            }
     317            nearbyNodeCacheDist = dist;
     318            if (nearbyNodeCache == null)
     319                nearbyNodeCache = Collections.emptySet();
     320            return nearbyNodeCache;
    184321        }
    185322
     
    191328    }
    192329
    193     @Override
    194     public void visit(Way w)
    195     {
     330    List<MyWaySegment> getWaySegments(Way w)
     331    {
     332        List<MyWaySegment> ret = new ArrayList<MyWaySegment>();
    196333        if (!w.isUsable()
    197334            || w.get("barrier") != null
    198335            || "cliff".equals(w.get("natural")))
    199             return;
     336            return ret;
    200337
    201338        int size = w.getNodesCount();
    202339        if(size < 2)
    203             return;
     340            return ret;
    204341        for(int i = 1; i < size; ++i)
    205342        {
    206343            if(i < size-1)
    207344                addNode(w.getNode(i), middlenodes);
    208             ways.add(new MyWaySegment(w, w.getNode(i-1), w.getNode(i)));
    209         }
     345            MyWaySegment ws = new MyWaySegment(w, w.getNode(i-1), w.getNode(i));
     346            if (ws.isBoundary || ws.isAbandoned)
     347                continue;
     348            ret.add(ws);
     349        }
     350        return ret;
     351    }
     352
     353    @Override
     354    public void visit(Way w)
     355    {
     356        ways.addAll(getWaySegments(w));
    210357        Set<Node> set = endnodes;
    211358        if(w.get("highway") != null || w.get("railway") != null)
    212359            set = endnodes_highway;
    213         addNode(w.getNode(0), set);
    214         addNode(w.getNode(size-1), set);
     360        addNode(w.firstNode(), set);
     361        addNode(w.lastNode(), set);
     362    }
     363    @Override
     364    public void visit(Node n)
     365    {
    215366    }
    216367    private void addNode(Node n, Set<Node> s)
Note: See TracChangeset for help on using the changeset viewer.