Ticket #2760: 2760-simplify-uninteresting-V2.diff

File 2760-simplify-uninteresting-V2.diff, 19.7 KB (added by Bjoeni, 6 years ago)
  • data/validator/unnecessary.mapcss

     
    5454
    5555*[emergency=permissive] {
    5656  /* 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}");
    5858  fixAdd: "emergency=yes";
    5959  assertMatch: "way emergency=permissive";
    6060  assertNoMatch: "way emergency=designated";
     
    144144  assertMatch: "way name=Rumah building=yes";
    145145  assertNoMatch: "way name=house building=house";
    146146  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";
    147156}
     157 No newline at end of file
  • src/org/openstreetmap/josm/actions/SimplifyWayAction.java

     
    55import static org.openstreetmap.josm.tools.I18n.tr;
    66import static org.openstreetmap.josm.tools.I18n.trn;
    77
     8import java.awt.GridBagLayout;
    89import java.awt.event.ActionEvent;
    910import java.awt.event.KeyEvent;
    1011import java.util.ArrayList;
     
    1718import java.util.Set;
    1819import java.util.stream.Collectors;
    1920
     21import javax.swing.BorderFactory;
     22import javax.swing.JCheckBox;
     23import javax.swing.JLabel;
    2024import javax.swing.JOptionPane;
     25import javax.swing.JPanel;
     26import javax.swing.JSpinner;
     27import javax.swing.SpinnerNumberModel;
    2128import javax.swing.SwingUtilities;
    2229
    2330import org.openstreetmap.josm.command.ChangeCommand;
     
    2431import org.openstreetmap.josm.command.Command;
    2532import org.openstreetmap.josm.command.DeleteCommand;
    2633import org.openstreetmap.josm.command.SequenceCommand;
     34import org.openstreetmap.josm.data.SystemOfMeasurement;
    2735import org.openstreetmap.josm.data.UndoRedoHandler;
    2836import org.openstreetmap.josm.data.osm.DataSet;
    2937import org.openstreetmap.josm.data.osm.Node;
     
    3038import org.openstreetmap.josm.data.osm.OsmPrimitive;
    3139import org.openstreetmap.josm.data.osm.Way;
    3240import org.openstreetmap.josm.data.projection.Ellipsoid;
     41import org.openstreetmap.josm.gui.ExtendedDialog;
    3342import org.openstreetmap.josm.gui.HelpAwareOptionPane;
    3443import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
    3544import org.openstreetmap.josm.gui.MainApplication;
    3645import org.openstreetmap.josm.gui.Notification;
    3746import org.openstreetmap.josm.spi.preferences.Config;
     47import org.openstreetmap.josm.spi.preferences.IPreferences;
     48import org.openstreetmap.josm.tools.GBC;
    3849import org.openstreetmap.josm.tools.ImageProvider;
    3950import org.openstreetmap.josm.tools.Shortcut;
    4051
     
    93104                );
    94105    }
    95106
     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
    96169    @Override
    97170    public void actionPerformed(ActionEvent e) {
    98171        DataSet ds = getLayerManager().getEditDataSet();
     
    108181                return;
    109182            }
    110183
    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);
    118197            }
    119             if (allCommands.isEmpty()) return;
    120             SequenceCommand rootCommand = new SequenceCommand(
    121                     trn("Simplify {0} way", "Simplify {0} ways", allCommands.size(), allCommands.size()),
    122                     allCommands
    123                     );
    124             UndoRedoHandler.getInstance().add(rootCommand);
    125198        } finally {
    126199            ds.endUpdate();
    127200        }
     
    156229    }
    157230
    158231    /**
    159      * Simplifies a way with default threshold (read from preferences).
    160      *
    161      * @param w the way to simplify
    162      * @return The sequence of commands to run
    163      * @since 6411
    164      */
    165     public final SequenceCommand simplifyWay(Way w) {
    166         return simplifyWay(w, Config.getPref().getDouble("simplify-way.max-error", 3.0));
    167     }
    168 
    169     /**
    170232     * Calculate a set of nodes which occurs more than once in the way
    171233     * @param w the way
    172234     * @return a set of nodes which occurs more than once in the way
     
    182244    }
    183245
    184246    /**
    185      * Simplifies a way with a given threshold.
     247     * Runs the commands to simplify the ways with the given threshold
    186248     *
     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     *
    187273     * @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
    188285     * @param threshold the max error threshold
    189286     * @return The sequence of commands to run
    190287     * @since 6411
  • src/org/openstreetmap/josm/data/gpx/GpxConstants.java

     
    1414 */
    1515public interface GpxConstants {
    1616
     17    /** Prefix used for attributes when converting to OSM data */
     18    String GPX_PREFIX = "gpx:";
     19
    1720    /** GPS name of the element. This field will be transferred to and from the GPS.
    1821     *  GPX does not place restrictions on the length of this field or the characters contained in it.
    1922     *  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

     
    1818import java.util.concurrent.TimeUnit;
    1919import java.util.concurrent.atomic.AtomicLong;
    2020
     21import org.openstreetmap.josm.data.gpx.GpxConstants;
    2122import org.openstreetmap.josm.spi.preferences.Config;
    2223import org.openstreetmap.josm.tools.Utils;
    2324
     
    741742        if (uninteresting == null) {
    742743            List<String> l = new LinkedList<>(Arrays.asList(
    743744                "source", "source_ref", "source:", "comment",
    744                 "watch", "watch:", "description", "attribution"));
     745                "watch", "watch:", "description", "attribution", GpxConstants.GPX_PREFIX));
    745746            l.addAll(getDiscardableKeys());
    746747            l.addAll(getWorkInProgressKeys());
    747748            uninteresting = new HashSet<>(Config.getPref().getList("tags.uninteresting", l));
  • src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java

     
    3131import java.util.LinkedHashMap;
    3232import java.util.List;
    3333import java.util.Map;
     34import java.util.Optional;
    3435import java.util.Set;
    3536import java.util.concurrent.CopyOnWriteArrayList;
    3637import java.util.concurrent.atomic.AtomicBoolean;
     
    748749            Collection<Collection<WayPoint>> trk = new ArrayList<>();
    749750            Map<String, Object> trkAttr = new HashMap<>();
    750751
    751             String name = w.get("name");
     752            String name = gpxVal(w, "name");
    752753            if (name != null) {
    753754                trkAttr.put("name", name);
    754755            }
     
    775776
    776777    private static boolean containsOnlyGpxTags(Tagged t) {
    777778        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)) {
    779780                return false;
    780781            }
    781782        }
     
    783784    }
    784785
    785786    /**
     787     * Reads the Gpx key from the given {@link OsmPrimitive}, with or without &quot;gpx:&quot; 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    /**
    786797     * @param n the {@code Node} to convert
    787798     * @return {@code WayPoint} object
    788799     * @since 13210
     
    805816        addDoubleIfPresent(wpt, n, GpxConstants.PT_ELE);
    806817
    807818        try {
     819            String v;
    808820            if (time > Long.MIN_VALUE) {
    809821                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));
    812824            } else if (!n.isTimestampEmpty()) {
    813825                wpt.setTime(Integer.toUnsignedLong(n.getRawTimestamp()));
    814826            }
     
    828840
    829841        Collection<GpxLink> links = new ArrayList<>();
    830842        for (String key : new String[]{"link", "url", "website", "contact:website"}) {
    831             String value = n.get(key);
     843            String value = gpxVal(n, key);
    832844            if (value != null) {
    833845                links.add(new GpxLink(value));
    834846            }
     
    866878        List<String> possibleKeys = new ArrayList<>(Arrays.asList(osmKeys));
    867879        possibleKeys.add(0, gpxKey);
    868880        for (String key : possibleKeys) {
    869             String value = p.get(key);
     881            String value = gpxVal(p, key);
    870882            if (value != null) {
    871883                try {
    872884                    int i = Integer.parseInt(value);
     
    887899        List<String> possibleKeys = new ArrayList<>(Arrays.asList(osmKeys));
    888900        possibleKeys.add(0, gpxKey);
    889901        for (String key : possibleKeys) {
    890             String value = p.get(key);
     902            String value = gpxVal(p, key);
    891903            if (value != null) {
    892904                try {
    893905                    double d = Double.parseDouble(value);
     
    907919        List<String> possibleKeys = new ArrayList<>(Arrays.asList(osmKeys));
    908920        possibleKeys.add(0, gpxKey);
    909921        for (String key : possibleKeys) {
    910             String value = p.get(key);
     922            String value = gpxVal(p, key);
    911923            // Sanity checks
    912924            if (value != null && (!GpxConstants.PT_FIX.equals(gpxKey) || GpxConstants.FIX_VALUES.contains(value))) {
    913925                wpt.put(gpxKey, value);
  • src/org/openstreetmap/josm/gui/layer/gpx/ConvertFromGpxLayerAction.java

     
    7474                            String str = (String) obj;
    7575                            if (!none) {
    7676                                // only convert when required
    77                                 n.put(key, str);
     77                                n.put(GpxConstants.GPX_PREFIX + key, str);
    7878                            }
    7979                        } else if (obj instanceof Date && GpxConstants.PT_TIME.equals(key)) {
    8080                            // timestamps should always be converted
    8181                            Date date = (Date) obj;
    8282                            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));
    8484                            }
    8585                            n.setTimestamp(date);
    8686                        }
  • src/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerAction.java

     
    77import java.awt.GridBagLayout;
    88import java.awt.event.ActionEvent;
    99import java.io.File;
     10import java.util.ArrayList;
    1011
    1112import javax.swing.AbstractAction;
    1213import javax.swing.JLabel;
     
    1314import javax.swing.JOptionPane;
    1415import javax.swing.JPanel;
    1516
     17import org.openstreetmap.josm.actions.SimplifyWayAction;
    1618import org.openstreetmap.josm.data.osm.DataSet;
    1719import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
    1820import org.openstreetmap.josm.gui.MainApplication;
     
    6264        }
    6365        final DataSet ds = convert();
    6466        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            }
    6571            final OsmDataLayer osmLayer = new OsmDataLayer(ds, tr("Converted from: {0}", layer.getName()), null);
    6672            if (layer.getAssociatedFile() != null) {
    6773                osmLayer.setAssociatedFile(new File(layer.getAssociatedFile().getParentFile(),
  • test/unit/org/openstreetmap/josm/actions/SimplifyWayActionTest.java

     
    129129        final Way w = new Way();
    130130        Stream.of(n1, n2, n3, n4, w).forEach(ds::addPrimitive);
    131131        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);
    133133        assertNotNull(command);
    134134        assertEquals(2, command.getChildren().size());
    135135        final Collection<DeleteCommand> deleteCommands = Utils.filteredCollection(command.getChildren(), DeleteCommand.class);
  • test/unit/org/openstreetmap/josm/gui/layer/OsmDataLayerTest.java

     
    215215                "<?xml version='1.0' encoding='UTF-8'?>\n" +
    216216                "<osm version='0.6' upload='false' generator='JOSM'>\n" +
    217217                "  <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" +
    220220                "  </node>\n" +
    221221                "  <node id='-546307' timestamp='2018-08-01T10:01:00Z' lat='47.1' lon='9.1'>\n" +
    222222                "    <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" +
    224224                "  </node>\n" +
    225225                "  <node id='-546308' timestamp='2018-08-01T10:02:00Z' lat='47.05' lon='9.05'>\n" +
    226226                "    <tag k='ele' v='789' />\n" +