source: josm/trunk/src/org/openstreetmap/josm/gui/io/importexport/GpxImporter.java@ 15593

Last change on this file since 15593 was 15593, checked in by GerdP, 4 years ago

fix #18397: Provide more helpful error message when loading an invalid gpx file
patch by Bjoeni

  • Property svn:eol-style set to native
File size: 8.4 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.io.File;
7import java.io.IOException;
8import java.io.InputStream;
9
10import javax.swing.JOptionPane;
11
12import org.openstreetmap.josm.actions.ExtensionFileFilter;
13import org.openstreetmap.josm.data.gpx.GpxData;
14import org.openstreetmap.josm.gui.MainApplication;
15import org.openstreetmap.josm.gui.Notification;
16import org.openstreetmap.josm.gui.layer.GpxLayer;
17import org.openstreetmap.josm.gui.layer.ImageryLayer;
18import org.openstreetmap.josm.gui.layer.OsmDataLayer;
19import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
20import org.openstreetmap.josm.gui.progress.ProgressMonitor;
21import org.openstreetmap.josm.gui.util.GuiHelper;
22import org.openstreetmap.josm.io.Compression;
23import org.openstreetmap.josm.io.GpxReader;
24import org.openstreetmap.josm.spi.preferences.Config;
25import org.openstreetmap.josm.tools.Logging;
26import org.xml.sax.SAXException;
27
28/**
29 * File importer allowing to import GPX files (*.gpx/gpx.gz files).
30 *
31 */
32public class GpxImporter extends FileImporter {
33
34 /**
35 * Utility class containing imported GPX and marker layers, and a task to run after they are added to MapView.
36 */
37 public static class GpxImporterData {
38 /**
39 * The imported GPX layer. May be null if no GPX data.
40 */
41 private final GpxLayer gpxLayer;
42 /**
43 * The imported marker layer. May be null if no marker.
44 */
45 private final MarkerLayer markerLayer;
46 /**
47 * The task to run after GPX and/or marker layer has been added to MapView.
48 */
49 private final Runnable postLayerTask;
50
51 /**
52 * Constructs a new {@code GpxImporterData}.
53 * @param gpxLayer The imported GPX layer. May be null if no GPX data.
54 * @param markerLayer The imported marker layer. May be null if no marker.
55 * @param postLayerTask The task to run after GPX and/or marker layer has been added to MapView.
56 */
57 public GpxImporterData(GpxLayer gpxLayer, MarkerLayer markerLayer, Runnable postLayerTask) {
58 this.gpxLayer = gpxLayer;
59 this.markerLayer = markerLayer;
60 this.postLayerTask = postLayerTask;
61 }
62
63 /**
64 * Returns the imported GPX layer. May be null if no GPX data.
65 * @return the imported GPX layer. May be null if no GPX data.
66 */
67 public GpxLayer getGpxLayer() {
68 return gpxLayer;
69 }
70
71 /**
72 * Returns the imported marker layer. May be null if no marker.
73 * @return the imported marker layer. May be null if no marker.
74 */
75 public MarkerLayer getMarkerLayer() {
76 return markerLayer;
77 }
78
79 /**
80 * Returns the task to run after GPX and/or marker layer has been added to MapView.
81 * @return the task to run after GPX and/or marker layer has been added to MapView.
82 */
83 public Runnable getPostLayerTask() {
84 return postLayerTask;
85 }
86 }
87
88 /**
89 * Constructs a new {@code GpxImporter}.
90 */
91 public GpxImporter() {
92 super(getFileFilter());
93 }
94
95 /**
96 * Returns a GPX file filter (*.gpx and *.gpx.gz files).
97 * @return a GPX file filter
98 */
99 public static ExtensionFileFilter getFileFilter() {
100 return ExtensionFileFilter.newFilterWithArchiveExtensions("gpx",
101 Config.getPref().get("save.extension.gpx", "gpx"), tr("GPX Files"), true);
102 }
103
104 @Override
105 public void importData(File file, ProgressMonitor progressMonitor) throws IOException {
106 final String fileName = file.getName();
107
108 try (InputStream is = Compression.getUncompressedFileInputStream(file)) {
109 GpxReader r = new GpxReader(is);
110 boolean parsedProperly = r.parse(true);
111 r.getGpxData().storageFile = file;
112 addLayers(loadLayers(r.getGpxData(), parsedProperly, fileName, tr("Markers from {0}", fileName)));
113 } catch (SAXException e) {
114 Logging.error(e);
115 throw new IOException(e.getLocalizedMessage(), e);
116 }
117 }
118
119 /**
120 * Adds the specified GPX and marker layers to Map.main
121 * @param data The layers to add
122 * @see #loadLayers
123 */
124 public static void addLayers(final GpxImporterData data) {
125 // FIXME: remove UI stuff from the IO subsystem
126 GuiHelper.runInEDT(() -> {
127 if (data.markerLayer != null) {
128 MainApplication.getLayerManager().addLayer(data.markerLayer);
129 }
130 if (data.gpxLayer != null) {
131 MainApplication.getLayerManager().addLayer(data.gpxLayer);
132 }
133 data.postLayerTask.run();
134 });
135 }
136
137 /**
138 * Replies the new GPX and marker layers corresponding to the specified GPX data.
139 * @param data The GPX data
140 * @param parsedProperly True if GPX data has been properly parsed by {@link GpxReader#parse}
141 * @param gpxLayerName The GPX layer name
142 * @param markerLayerName The marker layer name
143 * @return the new GPX and marker layers corresponding to the specified GPX data, to be used with {@link #addLayers}
144 * @see #addLayers
145 */
146 public static GpxImporterData loadLayers(final GpxData data, final boolean parsedProperly,
147 final String gpxLayerName, String markerLayerName) {
148 GpxLayer gpxLayer = null;
149 MarkerLayer markerLayer = null;
150 gpxLayer = new GpxLayer(data, gpxLayerName, data.storageFile != null);
151 if (Config.getPref().getBoolean("marker.makeautomarkers", true) && !data.waypoints.isEmpty()) {
152 markerLayer = new MarkerLayer(data, markerLayerName, data.storageFile, gpxLayer);
153 if (markerLayer.data.isEmpty()) {
154 markerLayer = null;
155 } else {
156 gpxLayer.setLinkedMarkerLayer(markerLayer);
157 }
158 }
159
160 final boolean isSameColor = MainApplication.getLayerManager()
161 .getLayersOfType(ImageryLayer.class)
162 .stream().noneMatch(ImageryLayer::isVisible)
163 && data.getTracks().stream().anyMatch(t -> OsmDataLayer.getBackgroundColor().equals(t.getColor()));
164
165 Runnable postLayerTask = () -> {
166 if (!parsedProperly) {
167 String msg;
168 if (data.storageFile == null) {
169 msg = tr("Error occurred while parsing gpx data for layer ''{0}''. Only a part of the file will be available.",
170 gpxLayerName);
171 } else {
172 msg = tr("Error occurred while parsing gpx file ''{0}''. Only a part of the file will be available.",
173 data.storageFile.getPath());
174 }
175 JOptionPane.showMessageDialog(null, msg);
176 }
177 if (isSameColor) {
178 new Notification(tr("The imported track \"{0}\" might not be visible because it has the same color as the background." +
179 "<br>You can change this in the context menu of the imported layer.", gpxLayerName))
180 .setIcon(JOptionPane.WARNING_MESSAGE)
181 .setDuration(Notification.TIME_LONG)
182 .show();
183 }
184 };
185 return new GpxImporterData(gpxLayer, markerLayer, postLayerTask);
186 }
187
188 /**
189 * Replies the new GPX and marker layers corresponding to the specified GPX file.
190 * @param is input stream to GPX data
191 * @param associatedFile GPX file
192 * @param gpxLayerName The GPX layer name
193 * @param markerLayerName The marker layer name
194 * @param progressMonitor The progress monitor
195 * @return the new GPX and marker layers corresponding to the specified GPX file
196 * @throws IOException if an I/O error occurs
197 */
198 public static GpxImporterData loadLayers(InputStream is, final File associatedFile,
199 final String gpxLayerName, String markerLayerName, ProgressMonitor progressMonitor) throws IOException {
200 try {
201 final GpxReader r = new GpxReader(is);
202 final boolean parsedProperly = r.parse(true);
203 r.getGpxData().storageFile = associatedFile;
204 return loadLayers(r.getGpxData(), parsedProperly, gpxLayerName, markerLayerName);
205 } catch (SAXException e) {
206 Logging.error(e);
207 throw new IOException(tr("Parsing data for layer ''{0}'' failed", gpxLayerName), e);
208 }
209 }
210}
Note: See TracBrowser for help on using the repository browser.