source: josm/trunk/src/org/openstreetmap/josm/gui/layer/gpx/ConvertFromGpxLayerAction.java@ 14448

Last change on this file since 14448 was 14448, checked in by Don-vip, 5 years ago

see #16995 - proper fix to timestamp formatting in GPX files, does not append trailing subsecond zeroes (patch by cmuelle8)

  • Property svn:eol-style set to native
File size: 10.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer.gpx;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.GridBagLayout;
7import java.awt.event.ActionEvent;
8import java.awt.event.ActionListener;
9import java.text.DateFormat;
10import java.util.ArrayList;
11import java.util.Collection;
12import java.util.Date;
13import java.util.List;
14import java.util.Map.Entry;
15
16import javax.swing.BorderFactory;
17import javax.swing.ButtonGroup;
18import javax.swing.JCheckBox;
19import javax.swing.JLabel;
20import javax.swing.JPanel;
21import javax.swing.JRadioButton;
22
23import org.openstreetmap.josm.data.gpx.GpxConstants;
24import org.openstreetmap.josm.data.gpx.GpxTrack;
25import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
26import org.openstreetmap.josm.data.gpx.WayPoint;
27import org.openstreetmap.josm.data.osm.DataSet;
28import org.openstreetmap.josm.data.osm.Node;
29import org.openstreetmap.josm.data.osm.Way;
30import org.openstreetmap.josm.gui.ExtendedDialog;
31import org.openstreetmap.josm.gui.MainApplication;
32import org.openstreetmap.josm.gui.layer.GpxLayer;
33import org.openstreetmap.josm.gui.layer.OsmDataLayer;
34import org.openstreetmap.josm.spi.preferences.Config;
35import org.openstreetmap.josm.tools.GBC;
36import org.openstreetmap.josm.tools.date.DateUtils;
37
38/**
39 * Converts a {@link GpxLayer} to a {@link OsmDataLayer}.
40 * @since 14129 (extracted from {@link ConvertToDataLayerAction})
41 */
42public class ConvertFromGpxLayerAction extends ConvertToDataLayerAction<GpxLayer> {
43
44 private static final String GPX_SETTING = "gpx.convert-tags";
45
46 /**
47 * Creates a new {@code FromGpxLayer}.
48 * @param layer the source layer
49 */
50 public ConvertFromGpxLayerAction(GpxLayer layer) {
51 super(layer);
52 }
53
54 @Override
55 public DataSet convert() {
56 final DataSet ds = new DataSet();
57
58 List<String> keys = new ArrayList<>();
59 String convertTags = Config.getPref().get(GPX_SETTING, "ask");
60 boolean check = "list".equals(convertTags) || "ask".equals(convertTags);
61 boolean none = "no".equals(convertTags); // no need to convert tags when no dialog will be shown anyways
62
63 for (GpxTrack trk : layer.data.getTracks()) {
64 for (GpxTrackSegment segment : trk.getSegments()) {
65 List<Node> nodes = new ArrayList<>();
66 for (WayPoint p : segment.getWayPoints()) {
67 Node n = new Node(p.getCoor());
68 for (Entry<String, Object> entry : p.attr.entrySet()) {
69 String key = entry.getKey();
70 Object obj = p.get(key);
71 if (check && !keys.contains(key) && (obj instanceof String || obj instanceof Date)) {
72 keys.add(key);
73 }
74 if (obj instanceof String) {
75 String str = (String) obj;
76 if (!none) {
77 // only convert when required
78 n.put(key, str);
79 }
80 } else if (obj instanceof Date && GpxConstants.PT_TIME.equals(key)) {
81 // timestamps should always be converted
82 Date date = (Date) obj;
83 if (!none) { //... but the tag will only be set when required
84 n.put(key, DateUtils.fromDate(date));
85 }
86 n.setTimestamp(date);
87 }
88 }
89 ds.addPrimitive(n);
90 nodes.add(n);
91 }
92 Way w = new Way();
93 w.setNodes(nodes);
94 ds.addPrimitive(w);
95 }
96 }
97 //gpx.convert-tags: all, list, *ask, no
98 //gpx.convert-tags.last: *all, list, no
99 //gpx.convert-tags.list.yes
100 //gpx.convert-tags.list.no
101 List<String> listPos = Config.getPref().getList(GPX_SETTING + ".list.yes");
102 List<String> listNeg = Config.getPref().getList(GPX_SETTING + ".list.no");
103 if (check && !keys.isEmpty()) {
104 // Either "list" or "ask" was stored in the settings, so the Nodes have to be filtered after all tags have been processed
105 List<String> allTags = new ArrayList<>(listPos);
106 allTags.addAll(listNeg);
107 if (!allTags.containsAll(keys) || "ask".equals(convertTags)) {
108 // not all keys are in positive/negative list, so we have to ask the user
109 TagConversionDialogResponse res = showTagConversionDialog(keys, listPos, listNeg);
110 if (res.sel == null) {
111 return null;
112 }
113 listPos = res.listPos;
114
115 if ("no".equals(res.sel)) {
116 // User just chose not to convert any tags, but that was unknown before the initial conversion
117 return filterDataSet(ds, null);
118 } else if ("all".equals(res.sel)) {
119 return ds;
120 }
121 }
122 if (!listPos.containsAll(keys)) {
123 return filterDataSet(ds, listPos);
124 }
125 }
126 return ds;
127 }
128
129 /**
130 * Filters the tags of the given {@link DataSet}
131 * @param ds The {@link DataSet}
132 * @param listPos A {@code List<String>} containing the tags to be kept, can be {@code null} if all tags are to be removed
133 * @return The {@link DataSet}
134 * @since 14103
135 */
136 public DataSet filterDataSet(DataSet ds, List<String> listPos) {
137 Collection<Node> nodes = ds.getNodes();
138 for (Node n : nodes) {
139 for (String key : n.keySet()) {
140 if (listPos == null || !listPos.contains(key)) {
141 n.put(key, null);
142 }
143 }
144 }
145 return ds;
146 }
147
148 /**
149 * Shows the TagConversionDialog asking the user whether to keep all, some or no tags
150 * @param keys The keys present during the current conversion
151 * @param listPos The keys that were previously selected
152 * @param listNeg The keys that were previously unselected
153 * @return {@link TagConversionDialogResponse} containing the selection
154 */
155 private static TagConversionDialogResponse showTagConversionDialog(List<String> keys, List<String> listPos, List<String> listNeg) {
156 TagConversionDialogResponse res = new TagConversionDialogResponse(listPos, listNeg);
157 String lSel = Config.getPref().get(GPX_SETTING + ".last", "all");
158
159 JPanel p = new JPanel(new GridBagLayout());
160 ButtonGroup r = new ButtonGroup();
161
162 p.add(new JLabel(
163 tr("The GPX layer contains fields that can be converted to OSM tags. How would you like to proceed?")),
164 GBC.eol());
165 JRadioButton rAll = new JRadioButton(tr("Convert all fields"), "all".equals(lSel));
166 r.add(rAll);
167 p.add(rAll, GBC.eol());
168
169 JRadioButton rList = new JRadioButton(tr("Only convert the following fields:"), "list".equals(lSel));
170 r.add(rList);
171 p.add(rList, GBC.eol());
172
173 JPanel q = new JPanel();
174
175 List<JCheckBox> checkList = new ArrayList<>();
176 for (String key : keys) {
177 JCheckBox cTmp = new JCheckBox(key, !listNeg.contains(key));
178 checkList.add(cTmp);
179 q.add(cTmp);
180 }
181
182 q.setBorder(BorderFactory.createEmptyBorder(0, 20, 5, 0));
183 p.add(q, GBC.eol());
184
185 JRadioButton rNone = new JRadioButton(tr("Do not convert any fields"), "no".equals(lSel));
186 r.add(rNone);
187 p.add(rNone, GBC.eol());
188
189 ActionListener enabler = new TagConversionDialogRadioButtonActionListener(checkList, true);
190 ActionListener disabler = new TagConversionDialogRadioButtonActionListener(checkList, false);
191
192 if (!"list".equals(lSel)) {
193 disabler.actionPerformed(null);
194 }
195
196 rAll.addActionListener(disabler);
197 rList.addActionListener(enabler);
198 rNone.addActionListener(disabler);
199
200 ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(), tr("Options"),
201 tr("Convert"), tr("Convert and remember selection"), tr("Cancel"))
202 .setButtonIcons("exportgpx", "exportgpx", "cancel").setContent(p);
203 int ret = ed.showDialog().getValue();
204
205 if (ret == 1 || ret == 2) {
206 for (JCheckBox cItem : checkList) {
207 String key = cItem.getText();
208 if (cItem.isSelected()) {
209 if (!res.listPos.contains(key)) {
210 res.listPos.add(key);
211 }
212 res.listNeg.remove(key);
213 } else {
214 if (!res.listNeg.contains(key)) {
215 res.listNeg.add(key);
216 }
217 res.listPos.remove(key);
218 }
219 }
220 if (rAll.isSelected()) {
221 res.sel = "all";
222 } else if (rNone.isSelected()) {
223 res.sel = "no";
224 }
225 Config.getPref().put(GPX_SETTING + ".last", res.sel);
226 if (ret == 2) {
227 Config.getPref().put(GPX_SETTING, res.sel);
228 } else {
229 Config.getPref().put(GPX_SETTING, "ask");
230 }
231 Config.getPref().putList(GPX_SETTING + ".list.yes", res.listPos);
232 Config.getPref().putList(GPX_SETTING + ".list.no", res.listNeg);
233 } else {
234 res.sel = null;
235 }
236 return res;
237 }
238
239 private static class TagConversionDialogResponse {
240
241 final List<String> listPos;
242 final List<String> listNeg;
243 String sel = "list";
244
245 TagConversionDialogResponse(List<String> p, List<String> n) {
246 listPos = new ArrayList<>(p);
247 listNeg = new ArrayList<>(n);
248 }
249 }
250
251 private static class TagConversionDialogRadioButtonActionListener implements ActionListener {
252
253 private final boolean enable;
254 private final List<JCheckBox> checkList;
255
256 TagConversionDialogRadioButtonActionListener(List<JCheckBox> chks, boolean en) {
257 enable = en;
258 checkList = chks;
259 }
260
261 @Override
262 public void actionPerformed(ActionEvent arg0) {
263 for (JCheckBox ch : checkList) {
264 ch.setEnabled(enable);
265 }
266 }
267 }
268}
Note: See TracBrowser for help on using the repository browser.