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}