Changeset 15419 in josm


Ignore:
Timestamp:
2019-10-05T15:59:56+02:00 (2 months ago)
Author:
Don-vip
Message:

fix #2760 - simplify track during GPX layer -> data layer conversion (patch by Bjoeni)

Location:
trunk
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/data/validator/unnecessary.mapcss

    r15255 r15419  
    166166  assertMatch: "way name=house building=yes";
    167167}
     168
     169/* #2760 */
     170*[/^gpx:/] {
     171  throwWarning: tr("{0} should not be uploaded", "{0.key}");
     172  group: tr("unnecessary tag");
     173  fixRemove: "{0.key}";
     174  assertMatch: "node gpx:time=2018-01-01T12:00:00Z";
     175  assertNoMatch: "node source=gpx:foo";
     176}
  • trunk/src/org/openstreetmap/josm/actions/SimplifyWayAction.java

    r15152 r15419  
    66import static org.openstreetmap.josm.tools.I18n.trn;
    77
     8import java.awt.GridBagLayout;
    89import java.awt.event.ActionEvent;
    910import java.awt.event.KeyEvent;
     
    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
     
    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;
     
    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;
     
    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;
     
    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     * @since 15419
     113     */
     114    public static double askSimplifyWays(String text, boolean auto) {
     115        IPreferences s = Config.getPref();
     116        String key = "simplify-way." + (auto ? "auto." : "");
     117        String keyRemember = key + "remember";
     118        String keyError = key + "max-error";
     119
     120        String r = s.get(keyRemember, "ask");
     121        if (auto && "no".equals(r)) {
     122            return -1;
     123        } else if ("yes".equals(r)) {
     124            return s.getDouble(keyError, 3.0);
     125        }
     126
     127        JPanel p = new JPanel(new GridBagLayout());
     128        p.add(new JLabel("<html><body style=\"width: 375px;\">" + text + "<br><br>" +
     129                tr("This reduces unnecessary nodes along the way and is especially recommended if GPS tracks were recorded by time "
     130                 + "(e.g. one point per second) or when the accuracy was low (reduces \"zigzag\" tracks).")
     131                + "</body></html>"), GBC.eol());
     132        p.setBorder(BorderFactory.createEmptyBorder(5, 10, 10, 5));
     133        JPanel q = new JPanel(new GridBagLayout());
     134        q.add(new JLabel(tr("Maximum error (meters): ")));
     135        JSpinner n = new JSpinner(new SpinnerNumberModel(
     136                s.getDouble(keyError, 3.0), 0.01, 100, 0.5));
     137        q.add(n);
     138        q.setBorder(BorderFactory.createEmptyBorder(14, 0, 10, 0));
     139        p.add(q, GBC.eol());
     140        JCheckBox c = new JCheckBox(tr("Do not ask again"));
     141        p.add(c, GBC.eol());
     142
     143        ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(),
     144                tr("Simplify way"), tr("Simplify"),
     145                auto ? tr("Proceed without simplifying") : tr("Cancel"))
     146                .setContent(p)
     147                .configureContextsensitiveHelp(("Action/SimplifyWay"), true);
     148        if (auto) {
     149            ed.setButtonIcons("simplify", "ok");
     150        } else {
     151            ed.setButtonIcons("ok", "cancel");
     152        }
     153
     154        int ret = ed.showDialog().getValue();
     155        double val = (double) n.getValue();
     156        if (ret == 1) {
     157            s.putDouble(keyError, val);
     158            if (c.isSelected()) {
     159                s.put(keyRemember, "yes");
     160            }
     161            return val;
     162        } else {
     163            if (auto && c.isSelected()) { //do not remember cancel for manual simplify, otherwise nothing would happen
     164                s.put(keyRemember, "no");
     165            }
     166            return -1;
     167        }
     168    }
     169
    96170    @Override
    97171    public void actionPerformed(ActionEvent e) {
     
    109183            }
    110184
    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);
    118             }
    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);
     185            String lengthstr = SystemOfMeasurement.getSystemOfMeasurement().getDistText(
     186                    ways.stream().collect(
     187                            Collectors.summingDouble(w -> {
     188                                return w.getLength();
     189                            })));
     190
     191            double err = askSimplifyWays(trn(
     192                    "You are about to simplify {0} way with a total length of {1}.",
     193                    "You are about to simplify {0} ways with a total length of {1}.",
     194                    ways.size(), ways.size(), lengthstr), false);
     195
     196            if (err > 0) {
     197                simplifyWays(ways, err);
     198            }
    125199        } finally {
    126200            ds.endUpdate();
     
    157231
    158232    /**
    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     /**
    170233     * Calculate a set of nodes which occurs more than once in the way
    171234     * @param w the way
     
    183246
    184247    /**
    185      * Simplifies a way with a given threshold.
     248     * Runs the commands to simplify the ways with the given threshold
     249     *
     250     * @param ways the ways to simplify
     251     * @param threshold the max error threshold
     252     * @since 15419
     253     */
     254    public static void simplifyWays(List<Way> ways, double threshold) {
     255        Collection<Command> allCommands = new LinkedList<>();
     256        for (Way way : ways) {
     257            SequenceCommand simplifyCommand = createSimplifyCommand(way, threshold);
     258            if (simplifyCommand == null) {
     259                continue;
     260            }
     261            allCommands.add(simplifyCommand);
     262        }
     263        if (allCommands.isEmpty())
     264            return;
     265        SequenceCommand rootCommand = new SequenceCommand(
     266                trn("Simplify {0} way", "Simplify {0} ways", allCommands.size(), allCommands.size()),
     267                allCommands);
     268        UndoRedoHandler.getInstance().add(rootCommand);
     269    }
     270
     271    /**
     272     * Creates the SequenceCommand to simplify a way with default threshold.
     273     *
     274     * @param w the way to simplify
     275     * @return The sequence of commands to run
     276     * @since 6411
     277     * @deprecated Replaced by {@link #createSimplifyCommand(Way)}. You can also use {@link #simplifyWays(List, double)} directly.
     278     */
     279    @Deprecated
     280    public final SequenceCommand simplifyWay(Way w) {
     281        return createSimplifyCommand(w);
     282    }
     283
     284    /**
     285     * Creates the SequenceCommand to simplify a way with a given threshold.
    186286     *
    187287     * @param w the way to simplify
     
    189289     * @return The sequence of commands to run
    190290     * @since 6411
    191      */
     291     * @deprecated Replaced by {@link #createSimplifyCommand(Way, double)}. You can also use {@link #simplifyWays(List, double)} directly.
     292     */
     293    @Deprecated
    192294    public static SequenceCommand simplifyWay(Way w, double threshold) {
     295        return createSimplifyCommand(w, threshold);
     296    }
     297
     298    /**
     299     * Creates the SequenceCommand to simplify a way with default threshold.
     300     *
     301     * @param w the way to simplify
     302     * @return The sequence of commands to run
     303     * @since 15419
     304     */
     305    public final SequenceCommand createSimplifyCommand(Way w) {
     306        return createSimplifyCommand(w, Config.getPref().getDouble("simplify-way.max-error", 3.0));
     307    }
     308
     309    /**
     310     * Creates the SequenceCommand to simplify a way with a given threshold.
     311     *
     312     * @param w the way to simplify
     313     * @param threshold the max error threshold
     314     * @return The sequence of commands to run
     315     * @since 15419
     316     */
     317    public static SequenceCommand createSimplifyCommand(Way w, double threshold) {
    193318        int lower = 0;
    194319        int i = 0;
  • trunk/src/org/openstreetmap/josm/data/gpx/GpxConstants.java

    r15249 r15419  
    1414 */
    1515public interface GpxConstants {
     16
     17    /** Prefix used for attributes when converting to OSM data */
     18    String GPX_PREFIX = "gpx:";
    1619
    1720    /** GPS name of the element. This field will be transferred to and from the GPS.
  • trunk/src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java

    r15014 r15419  
    2121import java.util.function.BiPredicate;
    2222
     23import org.openstreetmap.josm.data.gpx.GpxConstants;
    2324import org.openstreetmap.josm.spi.preferences.Config;
    2425import org.openstreetmap.josm.tools.Utils;
     
    741742            List<String> l = new LinkedList<>(Arrays.asList(
    742743                "source", "source_ref", "source:", "comment",
    743                 "watch", "watch:", "description", "attribution"));
     744                "watch", "watch:", "description", "attribution", GpxConstants.GPX_PREFIX));
    744745            l.addAll(getDiscardableKeys());
    745746            l.addAll(getWorkInProgressKeys());
  • trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java

    r15280 r15419  
    3232import java.util.List;
    3333import java.util.Map;
     34import java.util.Optional;
    3435import java.util.Set;
    3536import java.util.concurrent.CopyOnWriteArrayList;
     
    780781            Map<String, Object> trkAttr = new HashMap<>();
    781782
    782             String name = w.get("name");
     783            String name = gpxVal(w, "name");
    783784            if (name != null) {
    784785                trkAttr.put("name", name);
     
    807808    private static boolean containsOnlyGpxTags(Tagged t) {
    808809        for (String key : t.getKeys().keySet()) {
    809             if (!GpxConstants.WPT_KEYS.contains(key)) {
     810            if (!GpxConstants.WPT_KEYS.contains(key) && !key.startsWith(GpxConstants.GPX_PREFIX)) {
    810811                return false;
    811812            }
    812813        }
    813814        return true;
     815    }
     816
     817    /**
     818     * Reads the Gpx key from the given {@link OsmPrimitive}, with or without &quot;gpx:&quot; prefix
     819     * @param node
     820     * @param key
     821     * @return the value or <code>null</code> if not present
     822     * @since 15419
     823     */
     824    public static String gpxVal(OsmPrimitive node, String key) {
     825        return Optional.ofNullable(node.get(GpxConstants.GPX_PREFIX + key)).orElse(node.get(key));
    814826    }
    815827
     
    837849
    838850        try {
     851            String v;
    839852            if (time > Long.MIN_VALUE) {
    840853                wpt.setTimeInMillis(time);
    841             } else if (n.hasKey(GpxConstants.PT_TIME)) {
    842                 wpt.setTimeInMillis(DateUtils.tsFromString(n.get(GpxConstants.PT_TIME)));
     854            } else if ((v = gpxVal(n, GpxConstants.PT_TIME)) != null) {
     855                wpt.setTimeInMillis(DateUtils.tsFromString(v));
    843856            } else if (!n.isTimestampEmpty()) {
    844857                wpt.setTime(Integer.toUnsignedLong(n.getRawTimestamp()));
     
    860873        Collection<GpxLink> links = new ArrayList<>();
    861874        for (String key : new String[]{"link", "url", "website", "contact:website"}) {
    862             String value = n.get(key);
     875            String value = gpxVal(n, key);
    863876            if (value != null) {
    864877                links.add(new GpxLink(value));
     
    898911        possibleKeys.add(0, gpxKey);
    899912        for (String key : possibleKeys) {
    900             String value = p.get(key);
     913            String value = gpxVal(p, key);
    901914            if (value != null) {
    902915                try {
     
    919932        possibleKeys.add(0, gpxKey);
    920933        for (String key : possibleKeys) {
    921             String value = p.get(key);
     934            String value = gpxVal(p, key);
    922935            if (value != null) {
    923936                try {
     
    939952        possibleKeys.add(0, gpxKey);
    940953        for (String key : possibleKeys) {
    941             String value = p.get(key);
     954            String value = gpxVal(p, key);
    942955            // Sanity checks
    943956            if (value != null && (!GpxConstants.PT_FIX.equals(gpxKey) || GpxConstants.FIX_VALUES.contains(value))) {
  • trunk/src/org/openstreetmap/josm/gui/layer/gpx/ConvertFromGpxLayerAction.java

    r15249 r15419  
    5555        final DataSet ds = new DataSet();
    5656
    57         List<String> keys = new ArrayList<>();
     57        List<String> keys = new ArrayList<>(); // note that items in this list don't have the GPX_PREFIX
    5858        String convertTags = Config.getPref().get(GPX_SETTING, "ask");
    5959        boolean check = "list".equals(convertTags) || "ask".equals(convertTags);
     
    7373                        if (!none && (obj instanceof String || obj instanceof Number)) {
    7474                            // only convert when required
    75                             n.put(key, obj.toString());
     75                            n.put(GpxConstants.GPX_PREFIX + key, obj.toString());
    7676                        } else if (obj instanceof Date && GpxConstants.PT_TIME.equals(key)) {
    7777                            // timestamps should always be converted
    7878                            Date date = (Date) obj;
    7979                            if (!none) { //... but the tag will only be set when required
    80                                 n.put(key, DateUtils.fromDate(date));
     80                                n.put(GpxConstants.GPX_PREFIX + key, DateUtils.fromDate(date));
    8181                            }
    8282                            n.setTimestamp(date);
     
    126126     * Filters the tags of the given {@link DataSet}
    127127     * @param ds The {@link DataSet}
    128      * @param listPos A {@code List<String>} containing the tags to be kept, can be {@code null} if all tags are to be removed
     128     * @param listPos A {@code List<String>} containing the tags (without prefix) to be kept, can be {@code null} if all tags are to be removed
    129129     * @return The {@link DataSet}
    130130     * @since 14103
     
    134134        for (Node n : nodes) {
    135135            for (String key : n.keySet()) {
    136                 if (listPos == null || !listPos.contains(key)) {
     136                if (listPos == null || !listPos.contains(key.substring(GpxConstants.GPX_PREFIX.length()))) {
    137137                    n.put(key, null);
    138138                }
  • trunk/src/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerAction.java

    r14153 r15419  
    88import java.awt.event.ActionEvent;
    99import java.io.File;
     10import java.util.ArrayList;
    1011
    1112import javax.swing.AbstractAction;
     
    1415import javax.swing.JPanel;
    1516
     17import org.openstreetmap.josm.actions.SimplifyWayAction;
    1618import org.openstreetmap.josm.data.osm.DataSet;
    1719import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
     
    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.simplifyWays(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) {
  • trunk/test/unit/org/openstreetmap/josm/actions/SimplifyWayActionTest.java

    r12643 r15419  
    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.createSimplifyCommand(w, 3);
    133133        assertNotNull(command);
    134134        assertEquals(2, command.getChildren().size());
  • trunk/test/unit/org/openstreetmap/josm/gui/layer/OsmDataLayerTest.java

    r14504 r15419  
    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" +
Note: See TracChangeset for help on using the changeset viewer.