Hi, I've been playing around with JOSM for a bit as a pratice to improve my Java skills.
One thing I noticed on the OSM lists and wiki, is the problem that some tagging proposals aren't accepted because the suggested tags are dependend on the direction of the way, for example tagging of features that are different on the "left" and "right" side of the way, which easily can lead to corrupted/wrong data if the way is reversed without adjusting them. That's why I've attemped implement a "tag corrector" feature, that suggests corrections to tags when a way is reversed. Currently it covers "oneway" and tags that start with "left:" or "right:" or end with ":left" and ":right", for example as used on borders. I've set it up in a way that allows to use the functionalty elsewhere, for example if there were a tag that where problematic when splitting a way. I've attached a patch, with the hope that someone could give their opinion on the idea in general and my implementation in particular. A next step would be to have corrections of relations in a simular manner. Robin
Index: src/org/openstreetmap/josm/actions/ReverseWayAction.java =================================================================== --- src/org/openstreetmap/josm/actions/ReverseWayAction.java (revision 682) +++ src/org/openstreetmap/josm/actions/ReverseWayAction.java (working copy) @@ -15,6 +15,7 @@ import org.openstreetmap.josm.command.ChangeCommand; import org.openstreetmap.josm.command.Command; import org.openstreetmap.josm.command.SequenceCommand; +import org.openstreetmap.josm.data.corrector.ReverseWayTagCorrector; import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.OsmPrimitive; @@ -46,13 +47,20 @@ tr("Please select at least one way.")); return; } - Collection<Command> c = new LinkedList<Command>(); - for (Way w : sel) { - Way wnew = new Way(w); + + boolean updateProperties = false; + ReverseWayTagCorrector reverseWayTagCorrector = new ReverseWayTagCorrector(); + Collection<Command> c = new LinkedList<Command>(); + for (Way w : sel) { + Way wnew = new Way(w); Collections.reverse(wnew.nodes); - c.add(new ChangeCommand(w, wnew)); - } - Main.main.undoRedo.add(new SequenceCommand(tr("Reverse ways"), c)); - Main.map.repaint(); + updateProperties = reverseWayTagCorrector.execute(w) || updateProperties; + c.add(new ChangeCommand(w, wnew)); + } + Main.main.undoRedo.add(new SequenceCommand(tr("Reverse ways"), c)); + if (updateProperties) { + // selectionChanged(Main.ds.getSelected()); + } + } } Index: src/org/openstreetmap/josm/data/corrector/TagCorrection.java =================================================================== --- src/org/openstreetmap/josm/data/corrector/TagCorrection.java (revision 0) +++ src/org/openstreetmap/josm/data/corrector/TagCorrection.java (revision 0) @@ -0,0 +1,41 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.data.corrector; + +public class TagCorrection { + + private final String oldKey; + private final String newKey; + private final String oldValue; + private final String newValue; + + public TagCorrection(String oldKey, String oldValue, String newKey, String newValue) { + this.oldKey = oldKey; + this.oldValue = oldValue; + this.newKey = oldKey; + this.newValue = newValue; + } + + public String getOldKey() { + return oldKey; + } + + public String getOldValue() { + return oldValue; + } + + public String getNewKey() { + return newKey; + } + + public String getNewValue() { + return newValue; + } + + public boolean isKeyChanged() { + return !newKey.equals(oldKey); + } + + public boolean isValueChanged() { + return !newValue.equals(oldValue); + } +} Index: src/org/openstreetmap/josm/data/corrector/ReverseWayTagCorrector.java =================================================================== --- src/org/openstreetmap/josm/data/corrector/ReverseWayTagCorrector.java (revision 0) +++ src/org/openstreetmap/josm/data/corrector/ReverseWayTagCorrector.java (revision 0) @@ -0,0 +1,58 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.data.corrector; + +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.openstreetmap.josm.data.osm.OSMUtils; +import org.openstreetmap.josm.data.osm.Way; + +public class ReverseWayTagCorrector extends TagCorrector<Way> { + + private static final Pattern leftRightStartRegex = Pattern.compile( + "^(left|right):.*", Pattern.CASE_INSENSITIVE); + + private static final Pattern leftRightEndRegex = Pattern.compile( + "*.:(left|right)$", Pattern.CASE_INSENSITIVE); + + @Override public boolean execute(Way way) { + + ArrayList<TagCorrection> tagCorrections = new ArrayList<TagCorrection>(); + + for (String key : way.keySet()) { + String newKey = key; + String value = way.get(key); + String newValue = value; + + if (key.equals("oneway")) { + if (value.equals("-1")) + newValue = "true"; + else { + Boolean boolValue = OSMUtils.getOSMBoolean(value); + if (boolValue != null && boolValue.booleanValue()) { + newValue = "-1"; + } + } + } else { + Matcher m = leftRightStartRegex.matcher(key); + if (!m.matches()) + m = leftRightEndRegex.matcher(key); + + if (m.matches()) { + String leftRight = m.group(1).toLowerCase(); + + newKey = key.substring(0, m.start(1)).concat( + leftRight.equals("left") ? "right" : "left") + .concat(key.substring(m.end(1))); + } + } + + if (key != newKey || value != newValue) + tagCorrections.add(new TagCorrection(key, value, newKey, newValue)); + } + + return applyCorrections(tagCorrections, way); + } + +} Index: src/org/openstreetmap/josm/data/corrector/TagCorrectionTable.java =================================================================== --- src/org/openstreetmap/josm/data/corrector/TagCorrectionTable.java (revision 0) +++ src/org/openstreetmap/josm/data/corrector/TagCorrectionTable.java (revision 0) @@ -0,0 +1,35 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.data.corrector; + +import java.awt.Dimension; +import java.util.List; + +import javax.swing.JTable; + +public class TagCorrectionTable extends JTable { + + private static TagCorrectionTableModel tagCorrectionTableModel; + + public static TagCorrectionTable create(List<TagCorrection> tagCorrections) { + + tagCorrectionTableModel = new TagCorrectionTableModel( + tagCorrections); + TagCorrectionTable table = new TagCorrectionTable(tagCorrectionTableModel); + table.setPreferredScrollableViewportSize( + new Dimension(400,70) + ); + table.getColumnModel().getColumn(4).setPreferredWidth(40); + table.setRowSelectionAllowed(false); + + return table; + } + + public TagCorrectionTable(TagCorrectionTableModel tagCorrectionTableModel) { + super(tagCorrectionTableModel); + } + + public TagCorrectionTableModel getTagCorrectionTableModel() { + return tagCorrectionTableModel; + } + +} Index: src/org/openstreetmap/josm/data/corrector/TagCorrectionTableModel.java =================================================================== --- src/org/openstreetmap/josm/data/corrector/TagCorrectionTableModel.java (revision 0) +++ src/org/openstreetmap/josm/data/corrector/TagCorrectionTableModel.java (revision 0) @@ -0,0 +1,86 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.data.corrector; + +import static org.openstreetmap.josm.tools.I18n.tr; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.swing.table.AbstractTableModel; + +public class TagCorrectionTableModel extends AbstractTableModel { + + private List<TagCorrection> tagCorrections; + + private boolean[] apply; + + public TagCorrectionTableModel(List<TagCorrection> tagCorrections) { + this.tagCorrections = tagCorrections; + apply = new boolean[this.tagCorrections.size()]; + Arrays.fill(apply, true); + } + + @Override public int getColumnCount() { + return 5; + } + + @Override public Class<?> getColumnClass(int columnIndex) { + if (columnIndex == 4) + return Boolean.class; + return String.class; + } + + @Override public String getColumnName(int colIndex) { + switch (colIndex) { + case 0: + return tr("Old key"); + case 1: + return tr("Old value"); + case 2: + return tr("New key"); + case 3: + return tr("New value"); + case 4: + return tr("Apply?"); + } + return null; + } + + @Override public int getRowCount() { + return tagCorrections.size(); + } + + @Override public Object getValueAt(int rowIndex, int colIndex) { + + TagCorrection tagCorrection = tagCorrections.get(rowIndex); + + switch (colIndex) { + case 0: + return tagCorrection.getOldKey(); + case 1: + return tagCorrection.getOldValue(); + case 2: + return tagCorrection.getNewKey(); + case 3: + return tagCorrection.getNewValue(); + case 4: + return apply[rowIndex]; + } + return null; + } + + @Override public boolean isCellEditable(int rowIndex, int columnIndex) { + return columnIndex == 4; + } + + @Override public void setValueAt(Object aValue, int rowIndex, + int columnIndex) { + if (columnIndex == 4 && aValue instanceof Boolean) + apply[rowIndex] = (Boolean)aValue; + } + + public boolean getApply(int i) { + return apply[i]; + } +} Index: src/org/openstreetmap/josm/data/corrector/TagCorrector.java =================================================================== --- src/org/openstreetmap/josm/data/corrector/TagCorrector.java (revision 0) +++ src/org/openstreetmap/josm/data/corrector/TagCorrector.java (revision 0) @@ -0,0 +1,48 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.data.corrector; + +import static org.openstreetmap.josm.tools.I18n.tr; + +import java.util.List; + +import javax.swing.JOptionPane; +import javax.swing.JScrollPane; + +import org.openstreetmap.josm.Main; +import org.openstreetmap.josm.data.osm.OsmPrimitive; + +public abstract class TagCorrector<P extends OsmPrimitive> { + + public abstract boolean execute(P primitive); + + protected boolean applyCorrections(List<TagCorrection> tagCorrections, P primitive) { + + boolean updated = false; + + if (tagCorrections != null && tagCorrections.size() > 0) { + + final TagCorrectionTable table = TagCorrectionTable.create(tagCorrections); + final JScrollPane pane = new JScrollPane(table); + + int answer = JOptionPane.showConfirmDialog(Main.parent, pane, + tr("Select which keys should be changed."), + JOptionPane.OK_CANCEL_OPTION); + + if (answer == JOptionPane.OK_OPTION) { + for (int i = 0; i < tagCorrections.size(); i++) { + if (table.getTagCorrectionTableModel().getApply(i)) { + TagCorrection tagCorrection = tagCorrections.get(i); + if (tagCorrection.isKeyChanged()) + primitive.remove(tagCorrection.getOldKey()); + primitive.put(tagCorrection.getNewKey(), tagCorrection + .getNewValue()); + updated = true; + } + } + } + } + + return updated; + } + +} Index: src/org/openstreetmap/josm/data/osm/OSMUtils.java =================================================================== --- src/org/openstreetmap/josm/data/osm/OSMUtils.java (revision 0) +++ src/org/openstreetmap/josm/data/osm/OSMUtils.java (revision 0) @@ -0,0 +1,21 @@ +// License: GPL. For details, see LICENSE file. +package org.openstreetmap.josm.data.osm; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Locale; + +public class OSMUtils { + + static ArrayList<String> TRUE_VALUES = new ArrayList<String>(Arrays + .asList(new String[] { "true", "yes", "1", "on" })); + static ArrayList<String> FALSE_VALUES = new ArrayList<String>(Arrays + .asList(new String[] { "false", "no", "0", "off" })); + + public static Boolean getOSMBoolean(String value) { + String lowerValue = value.toLowerCase(Locale.ENGLISH); + if (TRUE_VALUES.contains(lowerValue)) return Boolean.TRUE; + if (FALSE_VALUES.contains(lowerValue)) return Boolean.FALSE; + return null; + } +}
_______________________________________________ josm-dev mailing list josm-dev@openstreetmap.org http://lists.openstreetmap.org/cgi-bin/mailman/listinfo/josm-dev