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}