001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.layer.gpx;
003
004import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
005import static org.openstreetmap.josm.tools.I18n.tr;
006
007import java.awt.GridBagLayout;
008import java.awt.event.ActionEvent;
009import java.io.File;
010import java.text.DateFormat;
011import java.util.ArrayList;
012import java.util.Arrays;
013import java.util.Collection;
014import java.util.Date;
015import java.util.Iterator;
016import java.util.List;
017import java.util.Map.Entry;
018import java.util.Optional;
019
020import javax.swing.AbstractAction;
021import javax.swing.JLabel;
022import javax.swing.JOptionPane;
023import javax.swing.JPanel;
024
025import org.openstreetmap.josm.Main;
026import org.openstreetmap.josm.data.gpx.GpxConstants;
027import org.openstreetmap.josm.data.gpx.GpxTrack;
028import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
029import org.openstreetmap.josm.data.gpx.WayPoint;
030import org.openstreetmap.josm.data.osm.DataSet;
031import org.openstreetmap.josm.data.osm.Node;
032import org.openstreetmap.josm.data.osm.Way;
033import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
034import org.openstreetmap.josm.gui.MainApplication;
035import org.openstreetmap.josm.gui.layer.GpxLayer;
036import org.openstreetmap.josm.gui.layer.Layer;
037import org.openstreetmap.josm.gui.layer.OsmDataLayer;
038import org.openstreetmap.josm.gui.layer.markerlayer.Marker;
039import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
040import org.openstreetmap.josm.gui.widgets.UrlLabel;
041import org.openstreetmap.josm.spi.preferences.Config;
042import org.openstreetmap.josm.tools.GBC;
043import org.openstreetmap.josm.tools.ImageProvider;
044import org.openstreetmap.josm.tools.Logging;
045import org.openstreetmap.josm.tools.UncheckedParseException;
046import org.openstreetmap.josm.tools.date.DateUtils;
047
048/**
049 * An abstract action for a conversion from a {@code T} {@link Layer} to a {@link OsmDataLayer}.
050 * @param <T> the source layer class
051 */
052public abstract class ConvertToDataLayerAction<T extends Layer> extends AbstractAction {
053    /** source layer */
054    protected final transient T layer;
055
056    /**
057     * Constructs a new {@code ConvertToDataLayerAction}
058     * @param layer source layer
059     */
060    protected ConvertToDataLayerAction(final T layer) {
061        super(tr("Convert to data layer"));
062        new ImageProvider("converttoosm").getResource().attachImageIcon(this, true);
063        this.layer = layer;
064        putValue("help", ht("/Action/ConvertToDataLayer"));
065    }
066
067    /**
068     * Converts a {@link GpxLayer} to a {@link OsmDataLayer}.
069     */
070    public static class FromGpxLayer extends ConvertToDataLayerAction<GpxLayer> {
071
072        private final DateFormat timeFormatter = DateUtils.getGpxFormat();
073
074        /**
075         * Creates a new {@code FromGpxLayer}.
076         * @param layer the source layer
077         */
078        public FromGpxLayer(GpxLayer layer) {
079            super(layer);
080        }
081
082        @Override
083        public DataSet convert() {
084            final DataSet ds = new DataSet();
085            for (GpxTrack trk : layer.data.getTracks()) {
086                for (GpxTrackSegment segment : trk.getSegments()) {
087                    List<Node> nodes = new ArrayList<>();
088                    for (WayPoint p : segment.getWayPoints()) {
089                        Node n = new Node(p.getCoor());
090                        for (Entry<String, Object> entry : p.attr.entrySet()) {
091                            String key = entry.getKey();
092                            String str = p.getString(key);
093                            if (str != null) {
094                                n.put(key, str);
095                                if (GpxConstants.PT_TIME.equals(key)) {
096                                    try {
097                                        n.setTimestamp(DateUtils.fromString(str));
098                                    } catch (UncheckedParseException e) {
099                                        Logging.log(Logging.LEVEL_WARN, e);
100                                    }
101                                }
102                            } else {
103                                Object obj = p.get(key);
104                                if (obj instanceof Date && GpxConstants.PT_TIME.equals(key)) {
105                                    Date date = (Date) obj;
106                                    n.put(key, timeFormatter.format(date));
107                                    n.setTimestamp(date);
108                                }
109                            }
110                        }
111                        ds.addPrimitive(n);
112                        nodes.add(n);
113                    }
114                    Way w = new Way();
115                    w.setNodes(nodes);
116                    ds.addPrimitive(w);
117                }
118            }
119            return ds;
120        }
121    }
122
123    /**
124     * Converts a {@link MarkerLayer} to a {@link OsmDataLayer}.
125     */
126    public static class FromMarkerLayer extends ConvertToDataLayerAction<MarkerLayer> {
127
128        /**
129         * Converts a {@link MarkerLayer} to a {@link OsmDataLayer}.
130         * @param layer marker layer
131         */
132        public FromMarkerLayer(MarkerLayer layer) {
133            super(layer);
134        }
135
136        @Override
137        public DataSet convert() {
138            final DataSet ds = new DataSet();
139            for (Marker marker : layer.data) {
140                final Node node = new Node(marker.getCoor());
141                final Collection<String> mapping = Config.getPref().getList("gpx.to-osm-mapping", Arrays.asList(
142                        GpxConstants.GPX_NAME, "name",
143                        GpxConstants.GPX_DESC, "description",
144                        GpxConstants.GPX_CMT, "note",
145                        GpxConstants.GPX_SRC, "source",
146                        GpxConstants.PT_SYM, "gpxicon"));
147                if (mapping.size() % 2 == 0) {
148                    final Iterator<String> it = mapping.iterator();
149                    while (it.hasNext()) {
150                        final String gpxKey = it.next();
151                        final String osmKey = it.next();
152                        Optional.ofNullable(marker.getTemplateValue(gpxKey, false))
153                                .map(String::valueOf)
154                                .ifPresent(s -> node.put(osmKey, s));
155                    }
156                } else {
157                    Logging.warn("Invalid gpx.to-osm-mapping Einstein setting: expecting even number of entries");
158                }
159                ds.addPrimitive(node);
160            }
161            return ds;
162        }
163    }
164
165    /**
166     * Performs the conversion to a {@link DataSet}.
167     * @return the resulting dataset
168     */
169    public abstract DataSet convert();
170
171    @Override
172    public void actionPerformed(ActionEvent e) {
173        JPanel msg = new JPanel(new GridBagLayout());
174        msg.add(new JLabel(
175                tr("<html>Upload of unprocessed GPS data as map data is considered harmful.<br>"
176                        + "If you want to upload traces, look here:</html>")),
177                GBC.eol());
178        msg.add(new UrlLabel(Main.getOSMWebsite() + "/traces", 2), GBC.eop());
179        if (!ConditionalOptionPaneUtil.showConfirmationDialog("convert_to_data", Main.parent, msg, tr("Warning"),
180                JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, JOptionPane.OK_OPTION)) {
181            return;
182        }
183        final DataSet ds = convert();
184        final OsmDataLayer osmLayer = new OsmDataLayer(ds, tr("Converted from: {0}", layer.getName()), null);
185        if (layer.getAssociatedFile() != null) {
186            osmLayer.setAssociatedFile(new File(layer.getAssociatedFile().getParentFile(), layer.getAssociatedFile().getName() + ".osm"));
187        }
188        osmLayer.setUploadDiscouraged(true);
189        MainApplication.getLayerManager().addLayer(osmLayer, false);
190        MainApplication.getLayerManager().removeLayer(layer);
191    }
192}