source: josm/trunk/src/org/openstreetmap/josm/gui/io/importexport/GpxExporter.java@ 16629

Last change on this file since 16629 was 16629, checked in by simon04, 4 years ago

see #19334 - https://errorprone.info/bugpattern/JavaTimeDefaultTimeZone

  • Property svn:eol-style set to native
File size: 16.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.io.importexport;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.GridBagLayout;
7import java.awt.event.ActionListener;
8import java.awt.event.KeyAdapter;
9import java.awt.event.KeyEvent;
10import java.io.File;
11import java.io.IOException;
12import java.io.OutputStream;
13import java.text.MessageFormat;
14import java.time.Year;
15import java.time.ZoneId;
16import java.util.Optional;
17
18import javax.swing.JButton;
19import javax.swing.JCheckBox;
20import javax.swing.JLabel;
21import javax.swing.JList;
22import javax.swing.JOptionPane;
23import javax.swing.JPanel;
24import javax.swing.JScrollPane;
25import javax.swing.ListSelectionModel;
26
27import org.openstreetmap.josm.data.gpx.GpxConstants;
28import org.openstreetmap.josm.data.gpx.GpxData;
29import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
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.Layer;
34import org.openstreetmap.josm.gui.layer.OsmDataLayer;
35import org.openstreetmap.josm.gui.widgets.JosmTextArea;
36import org.openstreetmap.josm.gui.widgets.JosmTextField;
37import org.openstreetmap.josm.io.Compression;
38import org.openstreetmap.josm.io.GpxWriter;
39import org.openstreetmap.josm.spi.preferences.Config;
40import org.openstreetmap.josm.tools.CheckParameterUtil;
41import org.openstreetmap.josm.tools.GBC;
42
43/**
44 * Exports data to a .gpx file. Data may be native GPX or OSM data which will be converted.
45 * @since 1949
46 */
47public class GpxExporter extends FileExporter implements GpxConstants {
48
49 private static final String GPL_WARNING = "<html><font color='red' size='-2'>"
50 + tr("Note: GPL is not compatible with the OSM license. Do not upload GPL licensed tracks.") + "</html>";
51
52 private static final String[] LICENSES = {
53 "Creative Commons By-SA",
54 "Open Database License (ODbL)",
55 "public domain",
56 "GNU Lesser Public License (LGPL)",
57 "BSD License (MIT/X11)"};
58
59 private static final String[] URLS = {
60 "https://creativecommons.org/licenses/by-sa/3.0",
61 "http://opendatacommons.org/licenses/odbl/1.0",
62 "public domain",
63 "https://www.gnu.org/copyleft/lesser.html",
64 "http://www.opensource.org/licenses/bsd-license.php"};
65
66 /**
67 * Constructs a new {@code GpxExporter}.
68 */
69 public GpxExporter() {
70 super(GpxImporter.getFileFilter());
71 }
72
73 @Override
74 public boolean acceptFile(File pathname, Layer layer) {
75 if (!(layer instanceof OsmDataLayer) && !(layer instanceof GpxLayer))
76 return false;
77 return super.acceptFile(pathname, layer);
78 }
79
80 @Override
81 public void exportData(File file, Layer layer) throws IOException {
82 exportData(file, layer, false);
83 }
84
85 @Override
86 public void exportDataQuiet(File file, Layer layer) throws IOException {
87 exportData(file, layer, true);
88 }
89
90 private void exportData(File file, Layer layer, boolean quiet) throws IOException {
91 CheckParameterUtil.ensureParameterNotNull(layer, "layer");
92 if (!(layer instanceof OsmDataLayer) && !(layer instanceof GpxLayer))
93 throw new IllegalArgumentException(MessageFormat.format("Expected instance of OsmDataLayer or GpxLayer. Got ''{0}''.", layer
94 .getClass().getName()));
95 CheckParameterUtil.ensureParameterNotNull(file, "file");
96
97 String fn = file.getPath();
98 if (fn.indexOf('.') == -1) {
99 fn += ".gpx";
100 file = new File(fn);
101 }
102
103 GpxData gpxData;
104 if (quiet) {
105 gpxData = getGpxData(layer, file);
106 try (OutputStream fo = Compression.getCompressedFileOutputStream(file)) {
107 GpxWriter w = new GpxWriter(fo);
108 w.write(gpxData);
109 w.close();
110 fo.flush();
111 }
112 return;
113 }
114
115 // open the dialog asking for options
116 JPanel p = new JPanel(new GridBagLayout());
117
118 // At this moment, we only need to know the attributes of the GpxData,
119 // conversion of OsmDataLayer (if needed) will be done after the dialog is closed.
120 if (layer instanceof GpxLayer) {
121 gpxData = ((GpxLayer) layer).data;
122 } else {
123 gpxData = new GpxData();
124 }
125
126 p.add(new JLabel(tr("GPS track description")), GBC.eol());
127 JosmTextArea desc = new JosmTextArea(3, 40);
128 desc.setWrapStyleWord(true);
129 desc.setLineWrap(true);
130 desc.setText(gpxData.getString(META_DESC));
131 p.add(new JScrollPane(desc), GBC.eop().fill(GBC.BOTH));
132
133 JCheckBox author = new JCheckBox(tr("Add author information"), Config.getPref().getBoolean("lastAddAuthor", true));
134 p.add(author, GBC.eol());
135
136 JLabel nameLabel = new JLabel(tr("Real name"));
137 p.add(nameLabel, GBC.std().insets(10, 0, 5, 0));
138 JosmTextField authorName = new JosmTextField();
139 p.add(authorName, GBC.eol().fill(GBC.HORIZONTAL));
140 nameLabel.setLabelFor(authorName);
141
142 JLabel emailLabel = new JLabel(tr("E-Mail"));
143 p.add(emailLabel, GBC.std().insets(10, 0, 5, 0));
144 JosmTextField email = new JosmTextField();
145 p.add(email, GBC.eol().fill(GBC.HORIZONTAL));
146 emailLabel.setLabelFor(email);
147
148 JLabel copyrightLabel = new JLabel(tr("Copyright (URL)"));
149 p.add(copyrightLabel, GBC.std().insets(10, 0, 5, 0));
150 JosmTextField copyright = new JosmTextField();
151 p.add(copyright, GBC.std().fill(GBC.HORIZONTAL));
152 copyrightLabel.setLabelFor(copyright);
153
154 JButton predefined = new JButton(tr("Predefined"));
155 p.add(predefined, GBC.eol().insets(5, 0, 0, 0));
156
157 JLabel copyrightYearLabel = new JLabel(tr("Copyright year"));
158 p.add(copyrightYearLabel, GBC.std().insets(10, 0, 5, 5));
159 JosmTextField copyrightYear = new JosmTextField("");
160 p.add(copyrightYear, GBC.eol().fill(GBC.HORIZONTAL));
161 copyrightYearLabel.setLabelFor(copyrightYear);
162
163 JLabel warning = new JLabel("<html><font size='-2'>&nbsp;</html");
164 p.add(warning, GBC.eol().fill(GBC.HORIZONTAL).insets(15, 0, 0, 0));
165 addDependencies(gpxData, author, authorName, email, copyright, predefined, copyrightYear, nameLabel, emailLabel,
166 copyrightLabel, copyrightYearLabel, warning);
167
168 p.add(new JLabel(tr("Keywords")), GBC.eol());
169 JosmTextField keywords = new JosmTextField();
170 keywords.setText(gpxData.getString(META_KEYWORDS));
171 p.add(keywords, GBC.eol().fill(GBC.HORIZONTAL));
172
173 boolean sel = Config.getPref().getBoolean("gpx.export.colors", true);
174 JCheckBox colors = new JCheckBox(tr("Save track colors in GPX file"), sel);
175 p.add(colors, GBC.eol().fill(GBC.HORIZONTAL));
176 JCheckBox garmin = new JCheckBox(tr("Use Garmin compatible GPX extensions"),
177 Config.getPref().getBoolean("gpx.export.colors.garmin", false));
178 garmin.setEnabled(sel);
179 p.add(garmin, GBC.eol().fill(GBC.HORIZONTAL).insets(20, 0, 0, 0));
180
181 boolean hasPrefs = !gpxData.getLayerPrefs().isEmpty();
182 JCheckBox layerPrefs = new JCheckBox(tr("Save layer specific preferences"),
183 hasPrefs && Config.getPref().getBoolean("gpx.export.prefs", true));
184 layerPrefs.setEnabled(hasPrefs);
185 p.add(layerPrefs, GBC.eop().fill(GBC.HORIZONTAL));
186
187 ExtendedDialog ed = new ExtendedDialog(MainApplication.getMainFrame(),
188 tr("Export options"),
189 tr("Export and Save"), tr("Cancel"))
190 .setButtonIcons("exportgpx", "cancel")
191 .setContent(p);
192
193 colors.addActionListener(l -> {
194 garmin.setEnabled(colors.isSelected());
195 });
196
197 garmin.addActionListener(l -> {
198 if (garmin.isSelected() && !ConditionalOptionPaneUtil.showConfirmationDialog(
199 "gpx_color_garmin",
200 ed,
201 new JLabel("<html>" + tr("Garmin track extensions only support 16 colors.") + "<br>"
202 + tr("If you continue, the closest supported track color will be used.")
203 + "</html>"),
204 tr("Information"),
205 JOptionPane.OK_CANCEL_OPTION,
206 JOptionPane.INFORMATION_MESSAGE,
207 JOptionPane.OK_OPTION)) {
208 garmin.setSelected(false);
209 }
210 });
211
212 if (ed.showDialog().getValue() != 1) {
213 setCanceled(true);
214 return;
215 }
216 setCanceled(false);
217
218 Config.getPref().putBoolean("lastAddAuthor", author.isSelected());
219 if (!authorName.getText().isEmpty()) {
220 Config.getPref().put("lastAuthorName", authorName.getText());
221 }
222 if (!copyright.getText().isEmpty()) {
223 Config.getPref().put("lastCopyright", copyright.getText());
224 }
225 Config.getPref().putBoolean("gpx.export.colors", colors.isSelected());
226 Config.getPref().putBoolean("gpx.export.colors.garmin", garmin.isSelected());
227 if (hasPrefs) {
228 Config.getPref().putBoolean("gpx.export.prefs", layerPrefs.isSelected());
229 }
230 ColorFormat cFormat = null;
231 if (colors.isSelected()) {
232 cFormat = garmin.isSelected() ? ColorFormat.GPXX : ColorFormat.GPXD;
233 }
234
235 gpxData = getGpxData(layer, file);
236
237 // add author and copyright details to the gpx data
238 if (author.isSelected()) {
239 if (!authorName.getText().isEmpty()) {
240 gpxData.put(META_AUTHOR_NAME, authorName.getText());
241 gpxData.put(META_COPYRIGHT_AUTHOR, authorName.getText());
242 }
243 if (!email.getText().isEmpty()) {
244 gpxData.put(META_AUTHOR_EMAIL, email.getText());
245 }
246 if (!copyright.getText().isEmpty()) {
247 gpxData.put(META_COPYRIGHT_LICENSE, copyright.getText());
248 }
249 if (!copyrightYear.getText().isEmpty()) {
250 gpxData.put(META_COPYRIGHT_YEAR, copyrightYear.getText());
251 }
252 }
253
254 // add the description to the gpx data
255 if (!desc.getText().isEmpty()) {
256 gpxData.put(META_DESC, desc.getText());
257 }
258
259 // add keywords to the gpx data
260 if (!keywords.getText().isEmpty()) {
261 gpxData.put(META_KEYWORDS, keywords.getText());
262 }
263
264 try (OutputStream fo = Compression.getCompressedFileOutputStream(file)) {
265 GpxWriter w = new GpxWriter(fo);
266 w.write(gpxData, cFormat, layerPrefs.isSelected());
267 w.close();
268 fo.flush();
269 }
270 }
271
272 private static GpxData getGpxData(Layer layer, File file) {
273 if (layer instanceof OsmDataLayer) {
274 return ((OsmDataLayer) layer).toGpxData();
275 } else if (layer instanceof GpxLayer) {
276 return ((GpxLayer) layer).data;
277 }
278 return OsmDataLayer.toGpxData(MainApplication.getLayerManager().getEditDataSet(), file);
279 }
280
281 private static void enableCopyright(final GpxData data, final JosmTextField copyright, final JButton predefined,
282 final JosmTextField copyrightYear, final JLabel copyrightLabel, final JLabel copyrightYearLabel,
283 final JLabel warning, boolean enable) {
284 copyright.setEnabled(enable);
285 predefined.setEnabled(enable);
286 copyrightYear.setEnabled(enable);
287 copyrightLabel.setEnabled(enable);
288 copyrightYearLabel.setEnabled(enable);
289 warning.setText(enable ? GPL_WARNING : "<html><font size='-2'>&nbsp;</html");
290
291 if (enable) {
292 if (copyrightYear.getText().isEmpty()) {
293 copyrightYear.setText(Optional.ofNullable(data.getString(META_COPYRIGHT_YEAR)).orElseGet(
294 () -> Year.now(ZoneId.systemDefault()).toString()));
295 }
296 if (copyright.getText().isEmpty()) {
297 copyright.setText(Optional.ofNullable(data.getString(META_COPYRIGHT_LICENSE)).orElseGet(
298 () -> Config.getPref().get("lastCopyright", "https://creativecommons.org/licenses/by-sa/2.5")));
299 copyright.setCaretPosition(0);
300 }
301 } else {
302 copyrightYear.setText("");
303 copyright.setText("");
304 }
305 }
306
307 // CHECKSTYLE.OFF: ParameterNumber
308
309 /**
310 * Add all those listeners to handle the enable state of the fields.
311 * @param data GPX data
312 * @param author Author checkbox
313 * @param authorName Author name textfield
314 * @param email E-mail textfield
315 * @param copyright Copyright textfield
316 * @param predefined Predefined button
317 * @param copyrightYear Copyright year textfield
318 * @param nameLabel Name label
319 * @param emailLabel E-mail label
320 * @param copyrightLabel Copyright label
321 * @param copyrightYearLabel Copyright year label
322 * @param warning Warning label
323 */
324 private static void addDependencies(
325 final GpxData data,
326 final JCheckBox author,
327 final JosmTextField authorName,
328 final JosmTextField email,
329 final JosmTextField copyright,
330 final JButton predefined,
331 final JosmTextField copyrightYear,
332 final JLabel nameLabel,
333 final JLabel emailLabel,
334 final JLabel copyrightLabel,
335 final JLabel copyrightYearLabel,
336 final JLabel warning) {
337
338 // CHECKSTYLE.ON: ParameterNumber
339 ActionListener authorActionListener = e -> {
340 boolean b = author.isSelected();
341 authorName.setEnabled(b);
342 email.setEnabled(b);
343 nameLabel.setEnabled(b);
344 emailLabel.setEnabled(b);
345 if (b) {
346 authorName.setText(Optional.ofNullable(data.getString(META_AUTHOR_NAME)).orElseGet(
347 () -> Config.getPref().get("lastAuthorName")));
348 email.setText(Optional.ofNullable(data.getString(META_AUTHOR_EMAIL)).orElseGet(
349 () -> Config.getPref().get("lastAuthorEmail")));
350 } else {
351 authorName.setText("");
352 email.setText("");
353 }
354 boolean isAuthorSet = !authorName.getText().isEmpty();
355 GpxExporter.enableCopyright(data, copyright, predefined, copyrightYear, copyrightLabel, copyrightYearLabel, warning,
356 b && isAuthorSet);
357 };
358 author.addActionListener(authorActionListener);
359
360 KeyAdapter authorNameListener = new KeyAdapter() {
361 @Override public void keyReleased(KeyEvent e) {
362 boolean b = !authorName.getText().isEmpty() && author.isSelected();
363 GpxExporter.enableCopyright(data, copyright, predefined, copyrightYear, copyrightLabel, copyrightYearLabel, warning, b);
364 }
365 };
366 authorName.addKeyListener(authorNameListener);
367
368 predefined.addActionListener(e -> {
369 JList<String> l = new JList<>(LICENSES);
370 l.setVisibleRowCount(LICENSES.length);
371 l.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
372 int answer = JOptionPane.showConfirmDialog(
373 MainApplication.getMainFrame(),
374 new JScrollPane(l),
375 tr("Choose a predefined license"),
376 JOptionPane.OK_CANCEL_OPTION,
377 JOptionPane.QUESTION_MESSAGE
378 );
379 if (answer != JOptionPane.OK_OPTION || l.getSelectedIndex() == -1)
380 return;
381 StringBuilder license = new StringBuilder();
382 for (int i : l.getSelectedIndices()) {
383 if (i == 2) {
384 license = new StringBuilder("public domain");
385 break;
386 }
387 if (license.length() > 0) {
388 license.append(", ");
389 }
390 license.append(URLS[i]);
391 }
392 copyright.setText(license.toString());
393 copyright.setCaretPosition(0);
394 });
395
396 authorActionListener.actionPerformed(null);
397 authorNameListener.keyReleased(null);
398 }
399}
Note: See TracBrowser for help on using the repository browser.