Ticket #5642: validator-sw-dr-check-ni.patch

File validator-sw-dr-check-ni.patch, 12.9 KB (added by bilbo, 11 years ago)

Patch to add checks for duplicated/same relations and same ways

  • src/org/openstreetmap/josm/plugins/validator/tests/SameWay.java

     
     1// License: GPL. See LICENSE file for details.
     2package org.openstreetmap.josm.plugins.validator.tests;
     3
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
     6import java.util.Collection;
     7import java.util.HashSet;
     8import java.util.LinkedList;
     9import java.util.List;
     10import java.util.Map;
     11import java.util.Vector;
     12
     13import org.openstreetmap.josm.Main;
     14import org.openstreetmap.josm.command.ChangeCommand;
     15import org.openstreetmap.josm.command.Command;
     16import org.openstreetmap.josm.command.DeleteCommand;
     17import org.openstreetmap.josm.command.SequenceCommand;
     18import org.openstreetmap.josm.data.coor.LatLon;
     19import org.openstreetmap.josm.data.osm.Node;
     20import org.openstreetmap.josm.data.osm.OsmPrimitive;
     21import org.openstreetmap.josm.data.osm.Relation;
     22import org.openstreetmap.josm.data.osm.RelationMember;
     23import org.openstreetmap.josm.data.osm.Way;
     24import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     25import org.openstreetmap.josm.plugins.validator.Severity;
     26import org.openstreetmap.josm.plugins.validator.Test;
     27import org.openstreetmap.josm.plugins.validator.TestError;
     28import org.openstreetmap.josm.plugins.validator.util.Bag;
     29/**
     30 * Tests if there are same ways (same position, but maybe different tags)
     31 */
     32public class SameWay extends Test
     33{
     34
     35    private static class WayPair {
     36        public List<LatLon> coor;
     37        public WayPair(List<LatLon> _coor) {
     38            coor=_coor;
     39        }
     40        @Override
     41        public int hashCode() {
     42            return coor.hashCode();
     43        }
     44        @Override
     45        public boolean equals(Object obj) {
     46            if (!(obj instanceof WayPair)) return false;
     47            WayPair wp = (WayPair) obj;
     48            return wp.coor.equals(coor);
     49        }
     50    }
     51
     52    protected static int SAME_WAY = 1901;
     53
     54    /** Bag of all ways */
     55    Bag<WayPair, OsmPrimitive> ways;
     56
     57    /**
     58     * Constructor
     59     */
     60    public SameWay()
     61    {
     62        super(tr("Same ways")+".",
     63              tr("This test checks that there are no ways with same node coordinates (tags may differ)."));
     64    }
     65
     66
     67    @Override
     68    public void startTest(ProgressMonitor monitor)
     69    {
     70        super.startTest(monitor);
     71        ways = new Bag<WayPair, OsmPrimitive>(1000);
     72    }
     73
     74    @Override
     75    public void endTest()
     76    {
     77        super.endTest();
     78        for(List<OsmPrimitive> sameway : ways.values() )
     79        {
     80            if( sameway.size() > 1)
     81            {
     82                TestError testError = new TestError(this, Severity.WARNING, tr("Ways with same position"), SAME_WAY, sameway);
     83                errors.add( testError );
     84            }
     85        }
     86        ways = null;
     87    }
     88
     89    @Override
     90    public void visit(Way w)
     91    {
     92        if( !w.isUsable() )
     93            return;
     94        List<Node> wNodes=w.getNodes();
     95        Vector<LatLon> wLat=new Vector<LatLon>(wNodes.size());
     96        for(int i=0;i<wNodes.size();i++) {
     97                 wLat.add(wNodes.get(i).getCoor());
     98        }
     99        WayPair wKey=new WayPair(wLat);
     100        ways.add(wKey, w);
     101    }
     102
     103    /**
     104     * No fix provided
     105     */
     106    @Override
     107    public Command fixError(TestError testError)
     108    {
     109        return null;
     110    }
     111
     112    @Override
     113    public boolean isFixable(TestError testError)
     114    {
     115        return false;
     116    }
     117}
  • src/org/openstreetmap/josm/plugins/validator/tests/DuplicateRelation.java

     
     1// License: GPL. See LICENSE file for details.
     2package org.openstreetmap.josm.plugins.validator.tests;
     3
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
     6import java.util.Collection;
     7import java.util.HashSet;
     8import java.util.LinkedList;
     9import java.util.List;
     10import java.util.Map;
     11import java.util.Vector;
     12
     13import org.openstreetmap.josm.Main;
     14import org.openstreetmap.josm.command.ChangeCommand;
     15import org.openstreetmap.josm.command.Command;
     16import org.openstreetmap.josm.command.DeleteCommand;
     17import org.openstreetmap.josm.command.SequenceCommand;
     18import org.openstreetmap.josm.data.coor.LatLon;
     19import org.openstreetmap.josm.data.osm.Node;
     20import org.openstreetmap.josm.data.osm.OsmPrimitive;
     21import org.openstreetmap.josm.data.osm.Relation;
     22import org.openstreetmap.josm.data.osm.RelationMember;
     23import org.openstreetmap.josm.data.osm.Way;
     24import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     25import org.openstreetmap.josm.plugins.validator.Severity;
     26import org.openstreetmap.josm.plugins.validator.Test;
     27import org.openstreetmap.josm.plugins.validator.TestError;
     28import org.openstreetmap.josm.plugins.validator.util.Bag;
     29/**
     30 * Tests if there are duplicate relations
     31 */
     32public class DuplicateRelation extends Test
     33{
     34
     35    private static class RelationPair {
     36        public List<RelationMember> members;
     37        public Map<String, String> keys;
     38        public RelationPair(List<RelationMember> _members,Map<String, String> _keys) {
     39            members=_members;
     40            keys=_keys;
     41        }
     42        @Override
     43        public int hashCode() {
     44            return members.hashCode()+keys.hashCode();
     45        }
     46        @Override
     47        public boolean equals(Object obj) {
     48            if (!(obj instanceof RelationPair)) return false;
     49            RelationPair rp = (RelationPair) obj;
     50            return rp.members.equals(members) && rp.keys.equals(keys);
     51        }
     52    }
     53
     54    protected static int DUPLICATE_RELATION = 2001;
     55    protected static int SAME_RELATION = 2002;
     56
     57    /** Bag of all relations */
     58    Bag<RelationPair, OsmPrimitive> relations;
     59
     60    /** Bag of all relations, regardless of keys */
     61    Bag<List<RelationMember>, OsmPrimitive> relations_nokeys;
     62
     63    /**
     64     * Constructor
     65     */
     66    public DuplicateRelation()
     67    {
     68        super(tr("Duplicated relations")+".",
     69              tr("This test checks that there are no relations with same tags and same members with same roles."));
     70    }
     71
     72
     73    @Override
     74    public void startTest(ProgressMonitor monitor)
     75    {
     76        super.startTest(monitor);
     77        relations = new Bag<RelationPair, OsmPrimitive>(1000);
     78        relations_nokeys = new Bag<List<RelationMember>, OsmPrimitive>(1000);
     79    }
     80
     81    @Override
     82    public void endTest()
     83    {
     84        super.endTest();
     85        for(List<OsmPrimitive> duplicated : relations.values() )
     86        {
     87            if( duplicated.size() > 1)
     88            {
     89                TestError testError = new TestError(this, Severity.ERROR, tr("Duplicated relations"), DUPLICATE_RELATION, duplicated);
     90                errors.add( testError );
     91            }
     92        }
     93        relations = null;
     94        for(List<OsmPrimitive> duplicated : relations_nokeys.values() )
     95        {
     96            if( duplicated.size() > 1)
     97            {
     98                TestError testError = new TestError(this, Severity.WARNING, tr("Relations with same members"), SAME_RELATION, duplicated);
     99                errors.add( testError );
     100            }
     101        }
     102        relations_nokeys = null;
     103    }
     104
     105    @Override
     106    public void visit(Relation r)
     107    {
     108        if( !r.isUsable() )
     109            return;
     110        List<RelationMember> rMembers=r.getMembers();
     111        Map<String, String> rkeys=r.getKeys();
     112        rkeys.remove("created_by");
     113        RelationPair rKey=new RelationPair(rMembers,rkeys);
     114        relations.add(rKey, r);
     115        relations_nokeys.add(rMembers, r);
     116    }
     117
     118    /**
     119     * Fix the error by removing all but one instance of duplicate relations
     120     */
     121    @Override
     122    public Command fixError(TestError testError)
     123    {
     124        if (testError.getCode() == SAME_RELATION) return null;
     125        Collection<? extends OsmPrimitive> sel = testError.getPrimitives();
     126        HashSet<Relation> rel_fix = new HashSet<Relation>();
     127
     128        for (OsmPrimitive osm : sel)
     129            if (osm instanceof Relation)
     130                rel_fix.add((Relation)osm);
     131
     132        if( rel_fix.size() < 2 )
     133            return null;
     134
     135        long idToKeep = 0;
     136        Relation relationToKeep = rel_fix.iterator().next();
     137        // Only one relation will be kept - the one with lowest positive ID, if such exist
     138        // or one "at random" if no such exists. Rest of the relations will be deleted
     139        for (Relation w: rel_fix) {
     140            if (!w.isNew()) {
     141                if (idToKeep == 0 || w.getId() < idToKeep) {
     142                    idToKeep = w.getId();
     143                    relationToKeep = w;
     144                }
     145            }
     146        }
     147
     148        // Find the relation that is member of one or more relations. (If any)
     149        Relation relationWithRelations = null;
     150        List<Relation> rel_ref = null;
     151        for (Relation w : rel_fix) {
     152            List<Relation> rel = OsmPrimitive.getFilteredList(w.getReferrers(), Relation.class);
     153            if (!rel.isEmpty()) {
     154                if (relationWithRelations != null)
     155                    throw new AssertionError("Cannot fix duplicate relations: More than one relation is member of another relation.");
     156                relationWithRelations = w;
     157                rel_ref = rel;
     158            }
     159        }
     160
     161        Collection<Command> commands = new LinkedList<Command>();
     162
     163        // Fix relations.
     164        if (relationWithRelations != null && relationToKeep != relationWithRelations) {
     165            for (Relation rel : rel_ref) {
     166                Relation newRel = new Relation(rel);
     167                for (int i = 0; i < newRel.getMembers().size(); ++i) {
     168                    RelationMember m = newRel.getMember(i);
     169                    if (relationWithRelations.equals(m.getMember())) {
     170                        newRel.setMember(i, new RelationMember(m.getRole(), relationToKeep));
     171                    }
     172                }
     173                commands.add(new ChangeCommand(rel, newRel));
     174            }
     175        }
     176
     177        //Delete all relations in the list
     178        rel_fix.remove(relationToKeep);
     179        commands.add(new DeleteCommand(rel_fix));
     180        return new SequenceCommand(tr("Delete duplicate relations"), commands);
     181    }
     182
     183    @Override
     184    public boolean isFixable(TestError testError)
     185    {
     186        if (!(testError.getTester() instanceof DuplicateRelation))
     187            return false;
     188
     189        if (testError.getCode() == SAME_RELATION) return false;
     190
     191        // We fix it only if there is no more than one relation that is relation member.
     192        Collection<? extends OsmPrimitive> sel = testError.getPrimitives();
     193        HashSet<Relation> relations = new HashSet<Relation>();
     194
     195        for (OsmPrimitive osm : sel)
     196            if (osm instanceof Relation)
     197                relations.add((Relation)osm);
     198
     199        if (relations.size() < 2)
     200            return false;
     201
     202        int relationsWithRelations = 0;
     203        for (Relation w : relations) {
     204            List<Relation> rel = OsmPrimitive.getFilteredList(w.getReferrers(), Relation.class);
     205            if (!rel.isEmpty()) {
     206                ++relationsWithRelations;
     207            }
     208        }
     209        return (relationsWithRelations <= 1);
     210    }
     211}
  • src/org/openstreetmap/josm/plugins/validator/OSMValidatorPlugin.java

     
    3838import org.openstreetmap.josm.plugins.validator.tests.CrossingWays;
    3939import org.openstreetmap.josm.plugins.validator.tests.DuplicateNode;
    4040import org.openstreetmap.josm.plugins.validator.tests.DuplicateWay;
     41import org.openstreetmap.josm.plugins.validator.tests.DuplicateRelation;
    4142import org.openstreetmap.josm.plugins.validator.tests.DuplicatedWayNodes;
    4243import org.openstreetmap.josm.plugins.validator.tests.MultipolygonTest;
    4344import org.openstreetmap.josm.plugins.validator.tests.NameMismatch;
    4445import org.openstreetmap.josm.plugins.validator.tests.NodesWithSameName;
     46import org.openstreetmap.josm.plugins.validator.tests.SameWay;
    4547import org.openstreetmap.josm.plugins.validator.tests.OverlappingWays;
    4648import org.openstreetmap.josm.plugins.validator.tests.RelationChecker;
    4749import org.openstreetmap.josm.plugins.validator.tests.SelfIntersectingWay;
     
    104106            MultipolygonTest.class, // ID  1601 ..  1699
    105107            RelationChecker.class, // ID  1701 ..  1799
    106108            TurnrestrictionTest.class, // ID  1801 ..  1899
     109            SameWay.class, // ID 1901 .. 1999
     110            DuplicateRelation.class, // ID 2001 .. 2099
    107111    };
    108112
    109113    /**