Ticket #2760: 2760-simplify-uninteresting-V2.diff
File 2760-simplify-uninteresting-V2.diff, 19.7 KB (added by , 6 years ago) |
---|
-
data/validator/unnecessary.mapcss
54 54 55 55 *[emergency=permissive] { 56 56 /* see #9458 - emergency=permissive makes no sense */ 57 throwWarning: tr("{0} makes no sense", "{0.tag ");57 throwWarning: tr("{0} makes no sense", "{0.tag}"); 58 58 fixAdd: "emergency=yes"; 59 59 assertMatch: "way emergency=permissive"; 60 60 assertNoMatch: "way emergency=designated"; … … 144 144 assertMatch: "way name=Rumah building=yes"; 145 145 assertNoMatch: "way name=house building=house"; 146 146 assertMatch: "way name=house building=yes"; 147 } 148 149 /* #2760 */ 150 *[/^gpx:/] { 151 throwWarning: tr("{0} should not be uploaded", "{0.key}"); 152 group: tr("unnecessary tag"); 153 fixRemove: "{0.key}"; 154 assertMatch: "node gpx:time=2018-01-01T12:00:00Z"; 155 assertNoMatch: "node source=gpx:foo"; 147 156 } 157 No newline at end of file -
src/org/openstreetmap/josm/actions/SimplifyWayAction.java
5 5 import static org.openstreetmap.josm.tools.I18n.tr; 6 6 import static org.openstreetmap.josm.tools.I18n.trn; 7 7 8 import java.awt.GridBagLayout; 8 9 import java.awt.event.ActionEvent; 9 10 import java.awt.event.KeyEvent; 10 11 import java.util.ArrayList; … … 17 18 import java.util.Set; 18 19 import java.util.stream.Collectors; 19 20 21 import javax.swing.BorderFactory; 22 import javax.swing.JCheckBox; 23 import javax.swing.JLabel; 20 24 import javax.swing.JOptionPane; 25 import javax.swing.JPanel; 26 import javax.swing.JSpinner; 27 import javax.swing.SpinnerNumberModel; 21 28 import javax.swing.SwingUtilities; 22 29 23 30 import org.openstreetmap.josm.command.ChangeCommand; … … 24 31 import org.openstreetmap.josm.command.Command; 25 32 import org.openstreetmap.josm.command.DeleteCommand; 26 33 import org.openstreetmap.josm.command.SequenceCommand; 34 import org.openstreetmap.josm.data.SystemOfMeasurement; 27 35 import org.openstreetmap.josm.data.UndoRedoHandler; 28 36 import org.openstreetmap.josm.data.osm.DataSet; 29 37 import org.openstreetmap.josm.data.osm.Node; … … 30 38 import org.openstreetmap.josm.data.osm.OsmPrimitive; 31 39 import org.openstreetmap.josm.data.osm.Way; 32 40 import org.openstreetmap.josm.data.projection.Ellipsoid; 41 import org.openstreetmap.josm.gui.ExtendedDialog; 33 42 import org.openstreetmap.josm.gui.HelpAwareOptionPane; 34 43 import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec; 35 44 import org.openstreetmap.josm.gui.MainApplication; 36 45 import org.openstreetmap.josm.gui.Notification; 37 46 import org.openstreetmap.josm.spi.preferences.Config; 47 import org.openstreetmap.josm.spi.preferences.IPreferences; 48 import org.openstreetmap.josm.tools.GBC; 38 49 import org.openstreetmap.josm.tools.ImageProvider; 39 50 import org.openstreetmap.josm.tools.Shortcut; 40 51 … … 93 104 ); 94 105 } 95 106 107 /** 108 * Asks the user for max-err value used to simplify ways, if not remembered before 109 * @param text the text being shown 110 * @param auto whether it's called automatically (conversion) or by the user 111 * @return the max-err value or -1 if canceled 112 */ 113 public static double askSimplifyWays(String text, boolean auto) { 114 IPreferences s = Config.getPref(); 115 String key = "simplify-way." + (auto ? "auto." : ""); 116 String keyRemember = key + "remember"; 117 String keyError = key + "max-error"; 118 119 String r = s.get(keyRemember, "ask"); 120 if (auto && "no".equals(r)) { 121 return -1; 122 } else if ("yes".equals(r)) { 123 return s.getDouble(keyError, 3.0); 124 } 125 126 JPanel p = new JPanel(new GridBagLayout()); 127 p.add(new JLabel("<html><body style=\"width: 375px;\">" + text + "<br><br>" + 128 tr("This reduces unnecessary nodes along the way and is especially recommended if GPS tracks were recorded by time " 129 + "(i.e. one point per second) or when the accuracy was low (reduces \"zigzag\" tracks).") 130 + "</body></html>"), GBC.eol()); 131 p.setBorder(BorderFactory.createEmptyBorder(5, 10, 10, 5)); 132 JPanel q = new JPanel(new GridBagLayout()); 133 q.add(new JLabel(tr("Maximum error (meters): "))); 134 JSpinner n = new JSpinner(new SpinnerNumberModel( 135 s.getDouble(keyError, 3.0), 0.01, 100, 0.5)); 136 q.add(n); 137 q.setBorder(BorderFactory.createEmptyBorder(14, 0, 10, 0)); 138 p.add(q, GBC.eol()); 139 JCheckBox c = new JCheckBox(tr("Do not ask again")); 140 p.add(c, GBC.eol()); 141 142 ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(), 143 tr("Simplify way"), tr("Simplify"), 144 auto ? tr("Proceed without simplifying") : tr("Cancel")) 145 .setContent(p) 146 .configureContextsensitiveHelp(("Action/SimplifyWay"), true); 147 if (auto) { 148 ed.setButtonIcons("simplify", "ok"); 149 } else { 150 ed.setButtonIcons("ok", "cancel"); 151 } 152 153 int ret = ed.showDialog().getValue(); 154 double val = (double) n.getValue(); 155 if (ret == 1) { 156 s.putDouble(keyError, val); 157 if (c.isSelected()) { 158 s.put(keyRemember, "yes"); 159 } 160 return val; 161 } else { 162 if (auto && c.isSelected()) { //do not remember cancel for manual simplify, otherwise nothing would happen 163 s.put(keyRemember, "no"); 164 } 165 return -1; 166 } 167 } 168 96 169 @Override 97 170 public void actionPerformed(ActionEvent e) { 98 171 DataSet ds = getLayerManager().getEditDataSet(); … … 108 181 return; 109 182 } 110 183 111 Collection<Command> allCommands = new LinkedList<>(); 112 for (Way way: ways) { 113 SequenceCommand simplifyCommand = simplifyWay(way); 114 if (simplifyCommand == null) { 115 continue; 116 } 117 allCommands.add(simplifyCommand); 184 String lengthstr = SystemOfMeasurement.getSystemOfMeasurement().getDistText( 185 ways.stream().collect( 186 Collectors.summingDouble(w -> { 187 return w.getLength(); 188 }))); 189 190 double err = askSimplifyWays(trn( 191 "You are about to simplify {0} way with a total length of {1}.", 192 "You are about to simplify {0} ways with a total length of {1}.", 193 ways.size(), ways.size(), lengthstr), false); 194 195 if (err > 0) { 196 doSimplifyWays(ways, err); 118 197 } 119 if (allCommands.isEmpty()) return;120 SequenceCommand rootCommand = new SequenceCommand(121 trn("Simplify {0} way", "Simplify {0} ways", allCommands.size(), allCommands.size()),122 allCommands123 );124 UndoRedoHandler.getInstance().add(rootCommand);125 198 } finally { 126 199 ds.endUpdate(); 127 200 } … … 156 229 } 157 230 158 231 /** 159 * Simplifies a way with default threshold (read from preferences).160 *161 * @param w the way to simplify162 * @return The sequence of commands to run163 * @since 6411164 */165 public final SequenceCommand simplifyWay(Way w) {166 return simplifyWay(w, Config.getPref().getDouble("simplify-way.max-error", 3.0));167 }168 169 /**170 232 * Calculate a set of nodes which occurs more than once in the way 171 233 * @param w the way 172 234 * @return a set of nodes which occurs more than once in the way … … 182 244 } 183 245 184 246 /** 185 * Simplifies a way with a given threshold.247 * Runs the commands to simplify the ways with the given threshold 186 248 * 249 * @param ways the ways to simplify 250 * @param threshold the max error threshold 251 * @since xxx 252 */ 253 public static void doSimplifyWays(List<Way> ways, double threshold) { 254 Collection<Command> allCommands = new LinkedList<>(); 255 for (Way way : ways) { 256 SequenceCommand simplifyCommand = simplifyWay(way, threshold); 257 if (simplifyCommand == null) { 258 continue; 259 } 260 allCommands.add(simplifyCommand); 261 } 262 if (allCommands.isEmpty()) 263 return; 264 SequenceCommand rootCommand = new SequenceCommand( 265 trn("Simplify {0} way", "Simplify {0} ways", allCommands.size(), allCommands.size()), 266 allCommands); 267 UndoRedoHandler.getInstance().add(rootCommand); 268 } 269 270 /** 271 * Creates the SequenceCommand to simplify a way with default threshold (read from preferences). 272 * 187 273 * @param w the way to simplify 274 * @return The sequence of commands to run 275 * @since 6411 276 */ 277 public final SequenceCommand simplifyWay(Way w) { 278 return simplifyWay(w, Config.getPref().getDouble("simplify-way.max-error", 3.0)); 279 } 280 281 /** 282 * Creates the SequenceCommand to simplify a way with a given threshold. 283 * 284 * @param w the way to simplify 188 285 * @param threshold the max error threshold 189 286 * @return The sequence of commands to run 190 287 * @since 6411 -
src/org/openstreetmap/josm/data/gpx/GpxConstants.java
14 14 */ 15 15 public interface GpxConstants { 16 16 17 /** Prefix used for attributes when converting to OSM data */ 18 String GPX_PREFIX = "gpx:"; 19 17 20 /** GPS name of the element. This field will be transferred to and from the GPS. 18 21 * GPX does not place restrictions on the length of this field or the characters contained in it. 19 22 * It is up to the receiving application to validate the field before sending it to the GPS. */ -
src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java
18 18 import java.util.concurrent.TimeUnit; 19 19 import java.util.concurrent.atomic.AtomicLong; 20 20 21 import org.openstreetmap.josm.data.gpx.GpxConstants; 21 22 import org.openstreetmap.josm.spi.preferences.Config; 22 23 import org.openstreetmap.josm.tools.Utils; 23 24 … … 741 742 if (uninteresting == null) { 742 743 List<String> l = new LinkedList<>(Arrays.asList( 743 744 "source", "source_ref", "source:", "comment", 744 "watch", "watch:", "description", "attribution" ));745 "watch", "watch:", "description", "attribution", GpxConstants.GPX_PREFIX)); 745 746 l.addAll(getDiscardableKeys()); 746 747 l.addAll(getWorkInProgressKeys()); 747 748 uninteresting = new HashSet<>(Config.getPref().getList("tags.uninteresting", l)); -
src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
31 31 import java.util.LinkedHashMap; 32 32 import java.util.List; 33 33 import java.util.Map; 34 import java.util.Optional; 34 35 import java.util.Set; 35 36 import java.util.concurrent.CopyOnWriteArrayList; 36 37 import java.util.concurrent.atomic.AtomicBoolean; … … 748 749 Collection<Collection<WayPoint>> trk = new ArrayList<>(); 749 750 Map<String, Object> trkAttr = new HashMap<>(); 750 751 751 String name = w.get("name");752 String name = gpxVal(w, "name"); 752 753 if (name != null) { 753 754 trkAttr.put("name", name); 754 755 } … … 775 776 776 777 private static boolean containsOnlyGpxTags(Tagged t) { 777 778 for (String key : t.getKeys().keySet()) { 778 if (!GpxConstants.WPT_KEYS.contains(key) ) {779 if (!GpxConstants.WPT_KEYS.contains(key) && !key.startsWith(GpxConstants.GPX_PREFIX)) { 779 780 return false; 780 781 } 781 782 } … … 783 784 } 784 785 785 786 /** 787 * Reads the Gpx key from the given {@link OsmPrimitive}, with or without "gpx:" prefix 788 * @param node 789 * @param key 790 * @return the value or <code>null</code> if not present 791 */ 792 public static String gpxVal(OsmPrimitive node, String key) { 793 return Optional.ofNullable(node.get(GpxConstants.GPX_PREFIX + key)).orElse(node.get(key)); 794 } 795 796 /** 786 797 * @param n the {@code Node} to convert 787 798 * @return {@code WayPoint} object 788 799 * @since 13210 … … 805 816 addDoubleIfPresent(wpt, n, GpxConstants.PT_ELE); 806 817 807 818 try { 819 String v; 808 820 if (time > Long.MIN_VALUE) { 809 821 wpt.setTimeInMillis(time); 810 } else if ( n.hasKey(GpxConstants.PT_TIME)) {811 wpt.setTimeInMillis(DateUtils.tsFromString( n.get(GpxConstants.PT_TIME)));822 } else if ((v = gpxVal(n, GpxConstants.PT_TIME)) != null) { 823 wpt.setTimeInMillis(DateUtils.tsFromString(v)); 812 824 } else if (!n.isTimestampEmpty()) { 813 825 wpt.setTime(Integer.toUnsignedLong(n.getRawTimestamp())); 814 826 } … … 828 840 829 841 Collection<GpxLink> links = new ArrayList<>(); 830 842 for (String key : new String[]{"link", "url", "website", "contact:website"}) { 831 String value = n.get(key);843 String value = gpxVal(n, key); 832 844 if (value != null) { 833 845 links.add(new GpxLink(value)); 834 846 } … … 866 878 List<String> possibleKeys = new ArrayList<>(Arrays.asList(osmKeys)); 867 879 possibleKeys.add(0, gpxKey); 868 880 for (String key : possibleKeys) { 869 String value = p.get(key);881 String value = gpxVal(p, key); 870 882 if (value != null) { 871 883 try { 872 884 int i = Integer.parseInt(value); … … 887 899 List<String> possibleKeys = new ArrayList<>(Arrays.asList(osmKeys)); 888 900 possibleKeys.add(0, gpxKey); 889 901 for (String key : possibleKeys) { 890 String value = p.get(key);902 String value = gpxVal(p, key); 891 903 if (value != null) { 892 904 try { 893 905 double d = Double.parseDouble(value); … … 907 919 List<String> possibleKeys = new ArrayList<>(Arrays.asList(osmKeys)); 908 920 possibleKeys.add(0, gpxKey); 909 921 for (String key : possibleKeys) { 910 String value = p.get(key);922 String value = gpxVal(p, key); 911 923 // Sanity checks 912 924 if (value != null && (!GpxConstants.PT_FIX.equals(gpxKey) || GpxConstants.FIX_VALUES.contains(value))) { 913 925 wpt.put(gpxKey, value); -
src/org/openstreetmap/josm/gui/layer/gpx/ConvertFromGpxLayerAction.java
74 74 String str = (String) obj; 75 75 if (!none) { 76 76 // only convert when required 77 n.put( key, str);77 n.put(GpxConstants.GPX_PREFIX + key, str); 78 78 } 79 79 } else if (obj instanceof Date && GpxConstants.PT_TIME.equals(key)) { 80 80 // timestamps should always be converted 81 81 Date date = (Date) obj; 82 82 if (!none) { //... but the tag will only be set when required 83 n.put( key, DateUtils.fromDate(date));83 n.put(GpxConstants.GPX_PREFIX + key, DateUtils.fromDate(date)); 84 84 } 85 85 n.setTimestamp(date); 86 86 } -
src/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerAction.java
7 7 import java.awt.GridBagLayout; 8 8 import java.awt.event.ActionEvent; 9 9 import java.io.File; 10 import java.util.ArrayList; 10 11 11 12 import javax.swing.AbstractAction; 12 13 import javax.swing.JLabel; … … 13 14 import javax.swing.JOptionPane; 14 15 import javax.swing.JPanel; 15 16 17 import org.openstreetmap.josm.actions.SimplifyWayAction; 16 18 import org.openstreetmap.josm.data.osm.DataSet; 17 19 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 18 20 import org.openstreetmap.josm.gui.MainApplication; … … 62 64 } 63 65 final DataSet ds = convert(); 64 66 if (ds != null) { 67 double err = SimplifyWayAction.askSimplifyWays(tr("Would you like to simplify the ways in the converted layer?"), true); 68 if (err > 0) { 69 SimplifyWayAction.doSimplifyWays(new ArrayList<>(ds.getWays()), err); 70 } 65 71 final OsmDataLayer osmLayer = new OsmDataLayer(ds, tr("Converted from: {0}", layer.getName()), null); 66 72 if (layer.getAssociatedFile() != null) { 67 73 osmLayer.setAssociatedFile(new File(layer.getAssociatedFile().getParentFile(), -
test/unit/org/openstreetmap/josm/actions/SimplifyWayActionTest.java
129 129 final Way w = new Way(); 130 130 Stream.of(n1, n2, n3, n4, w).forEach(ds::addPrimitive); 131 131 Stream.of(n1, n2, n3, n4, n1).forEach(w::addNode); 132 final SequenceCommand command = action.simplifyWay(w);132 final SequenceCommand command = SimplifyWayAction.simplifyWay(w, 3); 133 133 assertNotNull(command); 134 134 assertEquals(2, command.getChildren().size()); 135 135 final Collection<DeleteCommand> deleteCommands = Utils.filteredCollection(command.getChildren(), DeleteCommand.class); -
test/unit/org/openstreetmap/josm/gui/layer/OsmDataLayerTest.java
215 215 "<?xml version='1.0' encoding='UTF-8'?>\n" + 216 216 "<osm version='0.6' upload='false' generator='JOSM'>\n" + 217 217 " <node id='-546306' timestamp='2018-08-01T10:00:00Z' lat='47.0' lon='9.0'>\n" + 218 " <tag k=' ele' v='123' />\n" +219 " <tag k=' time' v='2018-08-01T10:00:00Z' />\n" +218 " <tag k='gpx:ele' v='123' />\n" + 219 " <tag k='gpx:time' v='2018-08-01T10:00:00Z' />\n" + 220 220 " </node>\n" + 221 221 " <node id='-546307' timestamp='2018-08-01T10:01:00Z' lat='47.1' lon='9.1'>\n" + 222 222 " <tag k='ele' v='456' />\n" + 223 " <tag k=' time' v='2018-08-01T10:01:00Z' />\n" +223 " <tag k='gpx:time' v='2018-08-01T10:01:00Z' />\n" + 224 224 " </node>\n" + 225 225 " <node id='-546308' timestamp='2018-08-01T10:02:00Z' lat='47.05' lon='9.05'>\n" + 226 226 " <tag k='ele' v='789' />\n" +