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