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

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

global use of !Utils.isEmpty/isBlank

  • Property svn:eol-style set to native
File size: 13.0 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.time.Instant;
10import java.util.ArrayList;
11import java.util.List;
12import java.util.Map;
13import java.util.Map.Entry;
14
15import javax.swing.BorderFactory;
16import javax.swing.ButtonGroup;
17import javax.swing.JCheckBox;
18import javax.swing.JLabel;
19import javax.swing.JPanel;
20import javax.swing.JRadioButton;
21
22import org.openstreetmap.josm.data.gpx.GpxConstants;
23import org.openstreetmap.josm.data.gpx.GpxData;
24import org.openstreetmap.josm.data.gpx.GpxExtension;
25import org.openstreetmap.josm.data.gpx.GpxExtensionCollection;
26import org.openstreetmap.josm.data.gpx.IGpxTrack;
27import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
28import org.openstreetmap.josm.data.gpx.WayPoint;
29import org.openstreetmap.josm.data.osm.DataSet;
30import org.openstreetmap.josm.data.osm.Node;
31import org.openstreetmap.josm.data.osm.OsmPrimitive;
32import org.openstreetmap.josm.data.osm.Way;
33import org.openstreetmap.josm.gui.ExtendedDialog;
34import org.openstreetmap.josm.gui.MainApplication;
35import org.openstreetmap.josm.gui.layer.GpxLayer;
36import org.openstreetmap.josm.gui.layer.OsmDataLayer;
37import org.openstreetmap.josm.spi.preferences.Config;
38import org.openstreetmap.josm.tools.GBC;
39import org.openstreetmap.josm.tools.Utils;
40
41/**
42 * Converts a {@link GpxLayer} to a {@link OsmDataLayer}.
43 * @since 14129 (extracted from {@link ConvertToDataLayerAction})
44 */
45public class ConvertFromGpxLayerAction extends ConvertToDataLayerAction<GpxLayer> {
46
47 private static final String GPX_SETTING = "gpx.convert-tags";
48
49 /**
50 * Creates a new {@code FromGpxLayer}.
51 * @param layer the source layer
52 */
53 public ConvertFromGpxLayerAction(GpxLayer layer) {
54 super(layer);
55 }
56
57 @Override
58 public DataSet convert() {
59 return convert(layer.data, Config.getPref().get(GPX_SETTING, "ask"), GpxConstants.GPX_PREFIX);
60 }
61
62 /**
63 * Converts the given {@link GpxData} to a {@link DataSet}
64 * @param data GPX data to convert
65 * @param convertTags "list", "ask" or "no"
66 * @param gpxPrefix GPX prefix for tags
67 * @return the converted dataset
68 * @since 18078
69 */
70 public static DataSet convert(GpxData data, String convertTags, String gpxPrefix) {
71 final DataSet ds = new DataSet();
72 ds.setGPXNamespaces(data.getNamespaces());
73
74 List<String> keys = new ArrayList<>(); // note that items in this list don't have the GPX_PREFIX
75 boolean check = "list".equals(convertTags) || "ask".equals(convertTags);
76 boolean none = "no".equals(convertTags); // no need to convert tags when no dialog will be shown anyways
77
78 for (IGpxTrack trk : data.getTracks()) {
79 for (IGpxTrackSegment segment : trk.getSegments()) {
80 List<Node> nodes = new ArrayList<>();
81 for (WayPoint p : segment.getWayPoints()) {
82 Node n = new Node(p.getCoor());
83 addAttributes(p.getAttributes(), n, keys, check, none, gpxPrefix);
84 if (!none) {
85 addExtensions(p.getExtensions(), n, false, keys, check, gpxPrefix);
86 }
87 ds.addPrimitive(n);
88 nodes.add(n);
89 }
90 Way w = new Way();
91 w.setNodes(nodes);
92 addAttributes(trk.getAttributes(), w, keys, check, none, gpxPrefix);
93 addAttributes(segment.getAttributes(), w, keys, check, none, gpxPrefix);
94 if (!none) {
95 addExtensions(trk.getExtensions(), w, false, keys, check, gpxPrefix);
96 addExtensions(segment.getExtensions(), w, true, keys, check, gpxPrefix);
97 }
98 ds.addPrimitive(w);
99 }
100 }
101 //gpx.convert-tags: all, list, *ask, no
102 //gpx.convert-tags.last: *all, list, no
103 //gpx.convert-tags.list.yes
104 //gpx.convert-tags.list.no
105 List<String> listPos = Config.getPref().getList(GPX_SETTING + ".list.yes");
106 List<String> listNeg = Config.getPref().getList(GPX_SETTING + ".list.no");
107 if (check && !keys.isEmpty()) {
108 // Either "list" or "ask" was stored in the settings, so the Nodes have to be filtered after all tags have been processed
109 List<String> allTags = new ArrayList<>(listPos);
110 allTags.addAll(listNeg);
111 if (!allTags.containsAll(keys) || "ask".equals(convertTags)) {
112 // not all keys are in positive/negative list, so we have to ask the user
113 TagConversionDialogResponse res = showTagConversionDialog(keys, listPos, listNeg);
114 if (res.sel == null) {
115 return null;
116 }
117 listPos = res.listPos;
118
119 if ("no".equals(res.sel)) {
120 // User just chose not to convert any tags, but that was unknown before the initial conversion
121 return filterDataSet(ds, null, gpxPrefix);
122 } else if ("all".equals(res.sel)) {
123 return ds;
124 }
125 }
126 if (!listPos.containsAll(keys)) {
127 return filterDataSet(ds, listPos, gpxPrefix);
128 }
129 }
130 return ds;
131 }
132
133 private static void addAttributes(
134 Map<String, Object> attr, OsmPrimitive p, List<String> keys, boolean check, boolean none, String gpxPrefix) {
135 for (Entry<String, Object> entry : attr.entrySet()) {
136 String key = entry.getKey();
137 Object obj = entry.getValue();
138 if (check && !keys.contains(key) && (obj instanceof String || obj instanceof Number || obj instanceof Instant)) {
139 keys.add(key);
140 }
141 if (!none && (obj instanceof String || obj instanceof Number)) {
142 // only convert when required
143 p.put(gpxPrefix + key, obj.toString());
144 } else if (obj instanceof Instant && GpxConstants.PT_TIME.equals(key)) {
145 // timestamps should always be converted
146 Instant date = (Instant) obj;
147 if (!none) { //... but the tag will only be set when required
148 p.put(gpxPrefix + key, String.valueOf(date));
149 }
150 p.setInstant(date);
151 }
152 }
153 }
154
155 private static void addExtensions(
156 GpxExtensionCollection exts, OsmPrimitive p, boolean seg, List<String> keys, boolean check, String gpxPrefix) {
157 for (GpxExtension ext : exts) {
158 String value = ext.getValue();
159 if (!Utils.isEmpty(value)) {
160 String extpre = "extension:";
161 String pre = ext.getPrefix();
162 if (Utils.isEmpty(pre)) {
163 pre = "other";
164 }
165 // needs to be distinguished since both track and segment extensions are applied to the resulting way
166 String segpre = seg ? "segment:" : "";
167 String key = ext.getFlatKey();
168 String fullkey = gpxPrefix + extpre + pre + ":" + segpre + key;
169 if (GpxConstants.EXTENSION_ABBREVIATIONS.containsKey(fullkey)) {
170 fullkey = GpxConstants.EXTENSION_ABBREVIATIONS.get(fullkey);
171 }
172 if (check && !keys.contains(fullkey)) {
173 keys.add(fullkey);
174 }
175 p.put(fullkey, value);
176 }
177 addExtensions(ext.getExtensions(), p, seg, keys, check, gpxPrefix);
178 }
179 }
180
181 /**
182 * Filters the tags of the given {@link DataSet}
183 * @param ds The {@link DataSet}
184 * @param listPos A {@code List<String>} containing the tags (without prefix) to be kept, can be {@code null} if all tags are to be removed
185 * @param gpxPrefix The GPX prefix
186 * @return The {@link DataSet}
187 * @since 18078
188 */
189 public static DataSet filterDataSet(DataSet ds, List<String> listPos, String gpxPrefix) {
190 for (OsmPrimitive p : ds.getPrimitives(p -> p instanceof Node || p instanceof Way)) {
191 p.visitKeys((primitive, key, value) -> {
192 String listkey;
193 if (listPos != null && key.startsWith(gpxPrefix)) {
194 listkey = key.substring(gpxPrefix.length());
195 } else {
196 listkey = key;
197 }
198 if (listPos == null || !listPos.contains(listkey)) {
199 p.put(key, null);
200 }
201 });
202 }
203 return ds;
204 }
205
206 /**
207 * Shows the TagConversionDialog asking the user whether to keep all, some or no tags
208 * @param keys The keys present during the current conversion
209 * @param listPos The keys that were previously selected
210 * @param listNeg The keys that were previously unselected
211 * @return {@link TagConversionDialogResponse} containing the selection
212 */
213 private static TagConversionDialogResponse showTagConversionDialog(List<String> keys, List<String> listPos, List<String> listNeg) {
214 TagConversionDialogResponse res = new TagConversionDialogResponse(listPos, listNeg);
215 String lSel = Config.getPref().get(GPX_SETTING + ".last", "all");
216
217 JPanel p = new JPanel(new GridBagLayout());
218 ButtonGroup r = new ButtonGroup();
219
220 p.add(new JLabel(
221 tr("The GPX layer contains fields that can be converted to OSM tags. How would you like to proceed?")),
222 GBC.eol());
223 JRadioButton rAll = new JRadioButton(tr("Convert all fields"), "all".equals(lSel));
224 r.add(rAll);
225 p.add(rAll, GBC.eol());
226
227 JRadioButton rList = new JRadioButton(tr("Only convert the following fields:"), "list".equals(lSel));
228 r.add(rList);
229 p.add(rList, GBC.eol());
230
231 JPanel q = new JPanel();
232
233 List<JCheckBox> checkList = new ArrayList<>();
234 for (String key : keys) {
235 JCheckBox cTmp = new JCheckBox(key, !listNeg.contains(key));
236 checkList.add(cTmp);
237 q.add(cTmp);
238 }
239
240 q.setBorder(BorderFactory.createEmptyBorder(0, 20, 5, 0));
241 p.add(q, GBC.eol());
242
243 JRadioButton rNone = new JRadioButton(tr("Do not convert any fields"), "no".equals(lSel));
244 r.add(rNone);
245 p.add(rNone, GBC.eol());
246
247 ActionListener enabler = new TagConversionDialogRadioButtonActionListener(checkList, true);
248 ActionListener disabler = new TagConversionDialogRadioButtonActionListener(checkList, false);
249
250 if (!"list".equals(lSel)) {
251 disabler.actionPerformed(null);
252 }
253
254 rAll.addActionListener(disabler);
255 rList.addActionListener(enabler);
256 rNone.addActionListener(disabler);
257
258 ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(), tr("Options"),
259 tr("Convert"), tr("Convert and remember selection"), tr("Cancel"))
260 .setButtonIcons("exportgpx", "exportgpx", "cancel").setContent(p);
261 int ret = ed.showDialog().getValue();
262
263 if (ret == 1 || ret == 2) {
264 for (JCheckBox cItem : checkList) {
265 String key = cItem.getText();
266 if (cItem.isSelected()) {
267 if (!res.listPos.contains(key)) {
268 res.listPos.add(key);
269 }
270 res.listNeg.remove(key);
271 } else {
272 if (!res.listNeg.contains(key)) {
273 res.listNeg.add(key);
274 }
275 res.listPos.remove(key);
276 }
277 }
278 if (rAll.isSelected()) {
279 res.sel = "all";
280 } else if (rNone.isSelected()) {
281 res.sel = "no";
282 }
283 Config.getPref().put(GPX_SETTING + ".last", res.sel);
284 if (ret == 2) {
285 Config.getPref().put(GPX_SETTING, res.sel);
286 } else {
287 Config.getPref().put(GPX_SETTING, "ask");
288 }
289 Config.getPref().putList(GPX_SETTING + ".list.yes", res.listPos);
290 Config.getPref().putList(GPX_SETTING + ".list.no", res.listNeg);
291 } else {
292 res.sel = null;
293 }
294 return res;
295 }
296
297 private static class TagConversionDialogResponse {
298
299 final List<String> listPos;
300 final List<String> listNeg;
301 String sel = "list";
302
303 TagConversionDialogResponse(List<String> p, List<String> n) {
304 listPos = new ArrayList<>(p);
305 listNeg = new ArrayList<>(n);
306 }
307 }
308
309 private static class TagConversionDialogRadioButtonActionListener implements ActionListener {
310
311 private final boolean enable;
312 private final List<JCheckBox> checkList;
313
314 TagConversionDialogRadioButtonActionListener(List<JCheckBox> chks, boolean en) {
315 enable = en;
316 checkList = chks;
317 }
318
319 @Override
320 public void actionPerformed(ActionEvent arg0) {
321 for (JCheckBox ch : checkList) {
322 ch.setEnabled(enable);
323 }
324 }
325 }
326}
Note: See TracBrowser for help on using the repository browser.