| 1 | package org.openstreetmap.josm.plugins.validator.tests; |
| 2 | |
| 3 | import static org.openstreetmap.josm.tools.I18n.tr; |
| 4 | |
| 5 | import java.util.Collection; |
| 6 | import java.util.LinkedList; |
| 7 | import java.util.List; |
| 8 | import java.util.Vector; |
| 9 | import java.util.Map; |
| 10 | |
| 11 | import org.openstreetmap.josm.Main; |
| 12 | import org.openstreetmap.josm.command.Command; |
| 13 | import org.openstreetmap.josm.command.DeleteCommand; |
| 14 | import org.openstreetmap.josm.command.SequenceCommand; |
| 15 | import org.openstreetmap.josm.data.coor.LatLon; |
| 16 | import org.openstreetmap.josm.data.osm.Node; |
| 17 | import org.openstreetmap.josm.data.osm.Way; |
| 18 | import org.openstreetmap.josm.data.osm.OsmPrimitive; |
| 19 | import org.openstreetmap.josm.plugins.validator.Severity; |
| 20 | import org.openstreetmap.josm.plugins.validator.Test; |
| 21 | import org.openstreetmap.josm.plugins.validator.TestError; |
| 22 | import org.openstreetmap.josm.plugins.validator.util.Bag; |
| 23 | /** |
| 24 | * Tests if there are duplicate ways |
| 25 | */ |
| 26 | public class DuplicateWay extends Test |
| 27 | { |
| 28 | |
| 29 | private class WayPair { |
| 30 | public List<LatLon> coor; |
| 31 | public Map<String, String> keys; |
| 32 | public WayPair(List<LatLon> _coor,Map<String, String> _keys) { |
| 33 | coor=_coor; |
| 34 | keys=_keys; |
| 35 | } |
| 36 | @Override |
| 37 | public int hashCode() { |
| 38 | return coor.hashCode()+keys.hashCode(); |
| 39 | } |
| 40 | @Override |
| 41 | public boolean equals(Object obj) { |
| 42 | if (!(obj instanceof WayPair)) return false; |
| 43 | WayPair wp = (WayPair) obj; |
| 44 | return wp.coor.equals(coor) && wp.keys.equals(keys); |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | protected static int DUPLICATE_WAY = 1; |
| 49 | |
| 50 | /** Bag of all ways */ |
| 51 | Bag<WayPair, OsmPrimitive> ways; |
| 52 | |
| 53 | /** |
| 54 | * Constructor |
| 55 | */ |
| 56 | public DuplicateWay() |
| 57 | { |
| 58 | super(tr("Duplicated ways")+".", |
| 59 | tr("This test checks that there are no ways with same tags and same node coordinates.")); |
| 60 | } |
| 61 | |
| 62 | |
| 63 | @Override |
| 64 | public void startTest() |
| 65 | { |
| 66 | ways = new Bag<WayPair, OsmPrimitive>(1000); |
| 67 | } |
| 68 | |
| 69 | @Override |
| 70 | public void endTest() |
| 71 | { |
| 72 | for(List<OsmPrimitive> duplicated : ways.values() ) |
| 73 | { |
| 74 | if( duplicated.size() > 1) |
| 75 | { |
| 76 | TestError testError = new TestError(this, Severity.ERROR, tr("Duplicated ways"), DUPLICATE_WAY, duplicated); |
| 77 | errors.add( testError ); |
| 78 | } |
| 79 | } |
| 80 | ways = null; |
| 81 | } |
| 82 | |
| 83 | @Override |
| 84 | public void visit(Way w) |
| 85 | { |
| 86 | if( w.deleted || w.incomplete ) |
| 87 | return; |
| 88 | List<Node> wNodes=w.getNodes(); |
| 89 | Vector<LatLon> wLat=new Vector<LatLon>(wNodes.size()); |
| 90 | for(int i=0;i<wNodes.size();i++) { |
| 91 | wLat.add(wNodes.get(i).getCoor()); |
| 92 | } |
| 93 | Map<String, String> wkeys=w.getKeys(); |
| 94 | wkeys.remove("created_by"); |
| 95 | WayPair wKey=new WayPair(wLat,wkeys); |
| 96 | ways.add(wKey, w); |
| 97 | } |
| 98 | |
| 99 | /** |
| 100 | * Fix the error by removing all but one instance of duplicate ways |
| 101 | */ |
| 102 | @Override |
| 103 | public Command fixError(TestError testError) |
| 104 | { |
| 105 | Collection<? extends OsmPrimitive> sel = testError.getPrimitives(); |
| 106 | LinkedList<Way> ways = new LinkedList<Way>(); |
| 107 | |
| 108 | for (OsmPrimitive osm : sel) |
| 109 | if (osm instanceof Way) |
| 110 | ways.add((Way)osm); |
| 111 | |
| 112 | if( ways.size() < 2 ) |
| 113 | return null; |
| 114 | |
| 115 | long idToKeep = 0; |
| 116 | // Only one way will be kept - the one with lowest positive ID, if such exist |
| 117 | // or one "at random" if no such exists. Rest of the ways will be deleted |
| 118 | for (Way w: ways) { |
| 119 | if (w.id > 0) { |
| 120 | if (idToKeep == 0 || w.id < idToKeep) idToKeep = w.id; |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | if (idToKeep > 0) { |
| 125 | //Remove chosen way from the list, rest of ways in the list will be deleted |
| 126 | for (Way w: ways) { |
| 127 | if (w.id == idToKeep) { |
| 128 | ways.remove(w); |
| 129 | break; |
| 130 | } |
| 131 | } |
| 132 | } else { |
| 133 | //Remove first way from the list, delete the rest |
| 134 | ways.remove(0); |
| 135 | } |
| 136 | |
| 137 | //Delete all ways in the list |
| 138 | //Note: nodes are not deleted, these can be detected and deleted at next pass |
| 139 | Collection<Command> commands = new LinkedList<Command>(); |
| 140 | commands.add(new DeleteCommand(ways)); |
| 141 | Main.main.undoRedo.add(new SequenceCommand(tr("Delete duplicate ways"), commands)); |
| 142 | return null; |
| 143 | } |
| 144 | |
| 145 | @Override |
| 146 | public boolean isFixable(TestError testError) |
| 147 | { |
| 148 | return (testError.getTester() instanceof DuplicateWay); |
| 149 | } |
| 150 | |
| 151 | } |