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", Arrays.asList(
110                        GpxConstants.GPX_NAME, "name",
111                        GpxConstants.GPX_DESC, "description",
112                        GpxConstants.GPX_CMT, "note",
113                        GpxConstants.GPX_SRC, "source",
114                        GpxConstants.PT_SYM, "gpxicon"));
115                if (mapping.size() % 2 == 0) {
116                    final Iterator<String> it = mapping.iterator();
117                    while (it.hasNext()) {
118                        final String gpxKey = it.next();
119                        final String osmKey = it.next();
120                        Optional.ofNullable(marker.getTemplateValue(gpxKey, false))
121                                .map(String::valueOf)
122                                .ifPresent(s -> node.put(osmKey, s));
123                    }
124                } else {
125                    Main.warn("Invalid gpx.to-osm-mapping Einstein setting: expecting even number of entries");
126                }
127                ds.addPrimitive(node);
128            }
129            return ds;
130        }
131    }
132
133    /**
134     * Performs the conversion to a {@link DataSet}.
135     * @return the resulting dataset
136     */
137    public abstract DataSet convert();
138
139    @Override
140    public void actionPerformed(ActionEvent e) {
141        JPanel msg = new JPanel(new GridBagLayout());
142        msg.add(new JLabel(
143                // CHECKSTYLE.OFF: LineLength
144                tr("<html>Upload of unprocessed GPS data as map data is considered harmful.<br>If you want to upload traces, look here:</html>")),
145                // CHECKSTYLE.ON: LineLength
146                GBC.eol());
147        msg.add(new UrlLabel(Main.getOSMWebsite() + "/traces", 2), GBC.eop());
148        if (!ConditionalOptionPaneUtil.showConfirmationDialog("convert_to_data", Main.parent, msg, tr("Warning"),
149                JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, JOptionPane.OK_OPTION)) {
150            return;
151        }
152        final DataSet ds = convert();
153        final OsmDataLayer layer = new OsmDataLayer(ds, tr("Converted from: {0}", this.layer.getName()), null);
154        if (this.layer.getAssociatedFile() != null) {
155            layer.setAssociatedFile(new File(this.layer.getAssociatedFile().getParentFile(), this.layer.getAssociatedFile().getName() + ".osm"));
156        }
157        layer.setUploadDiscouraged(true);
158        Main.getLayerManager().addLayer(layer);
159        Main.getLayerManager().removeLayer(this.layer);
160    }
161}