| | 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 | } |