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.util.ArrayList;
011import java.util.Arrays;
012import java.util.Collection;
013import java.util.Iterator;
014import java.util.List;
015import java.util.Optional;
016
017import javax.swing.AbstractAction;
018import javax.swing.JLabel;
019import javax.swing.JOptionPane;
020import javax.swing.JPanel;
021
022import org.openstreetmap.josm.Main;
023import org.openstreetmap.josm.data.gpx.GpxConstants;
024import org.openstreetmap.josm.data.gpx.GpxTrack;
025import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
026import org.openstreetmap.josm.data.gpx.WayPoint;
027import org.openstreetmap.josm.data.osm.DataSet;
028import org.openstreetmap.josm.data.osm.Node;
029import org.openstreetmap.josm.data.osm.Way;
030import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
031import org.openstreetmap.josm.gui.layer.GpxLayer;
032import org.openstreetmap.josm.gui.layer.Layer;
033import org.openstreetmap.josm.gui.layer.OsmDataLayer;
034import org.openstreetmap.josm.gui.layer.markerlayer.Marker;
035import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
036import org.openstreetmap.josm.gui.widgets.UrlLabel;
037import org.openstreetmap.josm.tools.GBC;
038import org.openstreetmap.josm.tools.ImageProvider;
039import org.openstreetmap.josm.tools.date.DateUtils;
040
041/**
042 * An abstract action for a conversion from a {@code T} {@link Layer} to a {@link OsmDataLayer}.
043 * @param <T> the source layer class
044 */
045public abstract class ConvertToDataLayerAction<T extends Layer> extends AbstractAction {
046    protected final transient T layer;
047
048    protected ConvertToDataLayerAction(final T layer) {
049        super(tr("Convert to data layer"), ImageProvider.get("converttoosm"));
050        this.layer = layer;
051        putValue("help", ht("/Action/ConvertToDataLayer"));
052    }
053
054    /**
055     * Converts a {@link GpxLayer} to a {@link OsmDataLayer}.
056     */
057    public static class FromGpxLayer extends ConvertToDataLayerAction<GpxLayer> {
058
059        /**
060         * Creates a new {@code FromGpxLayer}.
061         * @param layer the source layer
062         */
063        public FromGpxLayer(GpxLayer layer) {
064            super(layer);
065        }
066
067        @Override
068        public DataSet convert() {
069            final DataSet ds = new DataSet();
070            for (GpxTrack trk : layer.data.tracks) {
071                for (GpxTrackSegment segment : trk.getSegments()) {
072                    List<Node> nodes = new ArrayList<>();
073                    for (WayPoint p : segment.getWayPoints()) {
074                        Node n = new Node(p.getCoor());
075                        String timestr = p.getString(GpxConstants.PT_TIME);
076                        if (timestr != null) {
077                            n.setTimestamp(DateUtils.fromString(timestr));
078                        }
079                        ds.addPrimitive(n);
080                        nodes.add(n);
081                    }
082                    Way w = new Way();
083                    w.setNodes(nodes);
084                    ds.addPrimitive(w);
085                }
086            }
087            return ds;
088        }
089    }
090
091    /**
092     * Converts a {@link MarkerLayer} to a {@link OsmDataLayer}.
093     */
094    public static class FromMarkerLayer extends ConvertToDataLayerAction<MarkerLayer> {
095
096        /**
097         * Converts a {@link MarkerLayer} to a {@link OsmDataLayer}.
098         * @param layer marker layer
099         */
100        public FromMarkerLayer(MarkerLayer layer) {
101            super(layer);
102        }
103
104        @Override
105        public DataSet convert() {
106            final DataSet ds = new DataSet();
107            for (Marker marker : layer.data) {
108                final Node node = new Node(marker.getCoor());
109                final Collection<String> mapping = Main.pref.getCollection("gpx.to-osm-mapping",
110                        Arrays.asList("name", "name", "desc", "description", "cmt", "note", "sym", "gpxicon"));
111                if (mapping.size() % 2 == 0) {
112                    final Iterator<String> it = mapping.iterator();
113                    while (it.hasNext()) {
114                        final String gpxKey = it.next();
115                        final String osmKey = it.next();
116                        Optional.ofNullable(marker.getTemplateValue(gpxKey, false))
117                                .map(String::valueOf)
118                                .ifPresent(s -> node.put(osmKey, s));
119                    }
120                } else {
121                    Main.warn("Invalid gpx.to-osm-mapping Einstein setting: expecting even number of entries");
122                }
123                ds.addPrimitive(node);
124            }
125            return ds;
126        }
127    }
128
129    /**
130     * Performs the conversion to a {@link DataSet}.
131     * @return the resulting dataset
132     */
133    public abstract DataSet convert();
134
135    @Override
136    public void actionPerformed(ActionEvent e) {
137        JPanel msg = new JPanel(new GridBagLayout());
138        msg.add(new JLabel(
139                // CHECKSTYLE.OFF: LineLength
140                tr("<html>Upload of unprocessed GPS data as map data is considered harmful.<br>If you want to upload traces, look here:</html>")),
141                // CHECKSTYLE.ON: LineLength
142                GBC.eol());
143        msg.add(new UrlLabel(Main.getOSMWebsite() + "/traces", 2), GBC.eop());
144        if (!ConditionalOptionPaneUtil.showConfirmationDialog("convert_to_data", Main.parent, msg, tr("Warning"),
145                JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, JOptionPane.OK_OPTION)) {
146            return;
147        }
148        final DataSet ds = convert();
149        final OsmDataLayer layer = new OsmDataLayer(ds, tr("Converted from: {0}", this.layer.getName()), null);
150        if (this.layer.getAssociatedFile() != null) {
151            layer.setAssociatedFile(new File(this.layer.getAssociatedFile().getParentFile(), this.layer.getAssociatedFile().getName() + ".osm"));
152        }
153        layer.setUploadDiscouraged(true);
154        Main.getLayerManager().addLayer(layer);
155        Main.getLayerManager().removeLayer(this.layer);
156    }
157}