001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.io; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.io.File; 007import java.io.IOException; 008import java.io.InputStream; 009 010import javax.swing.JOptionPane; 011 012import org.openstreetmap.josm.Main; 013import org.openstreetmap.josm.actions.ExtensionFileFilter; 014import org.openstreetmap.josm.data.gpx.GpxData; 015import org.openstreetmap.josm.gui.layer.GpxLayer; 016import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer; 017import org.openstreetmap.josm.gui.progress.ProgressMonitor; 018import org.openstreetmap.josm.gui.util.GuiHelper; 019import org.xml.sax.SAXException; 020 021/** 022 * File importer allowing to import GPX files (*.gpx/gpx.gz files). 023 * 024 */ 025public class GpxImporter extends FileImporter { 026 027 /** 028 * Utility class containing imported GPX and marker layers, and a task to run after they are added to MapView. 029 */ 030 public static class GpxImporterData { 031 /** 032 * The imported GPX layer. May be null if no GPX data. 033 */ 034 private final GpxLayer gpxLayer; 035 /** 036 * The imported marker layer. May be null if no marker. 037 */ 038 private final MarkerLayer markerLayer; 039 /** 040 * The task to run after GPX and/or marker layer has been added to MapView. 041 */ 042 private final Runnable postLayerTask; 043 044 /** 045 * Constructs a new {@code GpxImporterData}. 046 * @param gpxLayer The imported GPX layer. May be null if no GPX data. 047 * @param markerLayer The imported marker layer. May be null if no marker. 048 * @param postLayerTask The task to run after GPX and/or marker layer has been added to MapView. 049 */ 050 public GpxImporterData(GpxLayer gpxLayer, MarkerLayer markerLayer, Runnable postLayerTask) { 051 this.gpxLayer = gpxLayer; 052 this.markerLayer = markerLayer; 053 this.postLayerTask = postLayerTask; 054 } 055 056 /** 057 * Returns the imported GPX layer. May be null if no GPX data. 058 * @return the imported GPX layer. May be null if no GPX data. 059 */ 060 public GpxLayer getGpxLayer() { 061 return gpxLayer; 062 } 063 064 /** 065 * Returns the imported marker layer. May be null if no marker. 066 * @return the imported marker layer. May be null if no marker. 067 */ 068 public MarkerLayer getMarkerLayer() { 069 return markerLayer; 070 } 071 072 /** 073 * Returns the task to run after GPX and/or marker layer has been added to MapView. 074 * @return the task to run after GPX and/or marker layer has been added to MapView. 075 */ 076 public Runnable getPostLayerTask() { 077 return postLayerTask; 078 } 079 } 080 081 /** 082 * Constructs a new {@code GpxImporter}. 083 */ 084 public GpxImporter() { 085 super(getFileFilter()); 086 } 087 088 /** 089 * Returns a GPX file filter (*.gpx and *.gpx.gz files). 090 * @return a GPX file filter 091 */ 092 public static ExtensionFileFilter getFileFilter() { 093 return ExtensionFileFilter.newFilterWithArchiveExtensions( 094 "gpx", Main.pref.get("save.extension.gpx", "gpx"), tr("GPX Files"), true); 095 } 096 097 @Override 098 public void importData(File file, ProgressMonitor progressMonitor) throws IOException { 099 final String fileName = file.getName(); 100 101 try (InputStream is = Compression.getUncompressedFileInputStream(file)) { 102 GpxReader r = new GpxReader(is); 103 boolean parsedProperly = r.parse(true); 104 r.getGpxData().storageFile = file; 105 addLayers(loadLayers(r.getGpxData(), parsedProperly, fileName, tr("Markers from {0}", fileName))); 106 } catch (SAXException e) { 107 Main.error(e); 108 throw new IOException(tr("Parsing data for layer ''{0}'' failed", fileName), e); 109 } 110 } 111 112 /** 113 * Adds the specified GPX and marker layers to Map.main 114 * @param data The layers to add 115 * @see #loadLayers 116 */ 117 public static void addLayers(final GpxImporterData data) { 118 // FIXME: remove UI stuff from the IO subsystem 119 GuiHelper.runInEDT(() -> { 120 if (data.markerLayer != null) { 121 Main.getLayerManager().addLayer(data.markerLayer); 122 } 123 if (data.gpxLayer != null) { 124 Main.getLayerManager().addLayer(data.gpxLayer); 125 } 126 data.postLayerTask.run(); 127 }); 128 } 129 130 /** 131 * Replies the new GPX and marker layers corresponding to the specified GPX data. 132 * @param data The GPX data 133 * @param parsedProperly True if GPX data has been properly parsed by {@link GpxReader#parse} 134 * @param gpxLayerName The GPX layer name 135 * @param markerLayerName The marker layer name 136 * @return the new GPX and marker layers corresponding to the specified GPX data, to be used with {@link #addLayers} 137 * @see #addLayers 138 */ 139 public static GpxImporterData loadLayers(final GpxData data, final boolean parsedProperly, 140 final String gpxLayerName, String markerLayerName) { 141 GpxLayer gpxLayer = null; 142 MarkerLayer markerLayer = null; 143 if (data.hasRoutePoints() || data.hasTrackPoints()) { 144 gpxLayer = new GpxLayer(data, gpxLayerName, data.storageFile != null); 145 } 146 if (Main.pref.getBoolean("marker.makeautomarkers", true) && !data.waypoints.isEmpty()) { 147 markerLayer = new MarkerLayer(data, markerLayerName, data.storageFile, gpxLayer); 148 if (markerLayer.data.isEmpty()) { 149 markerLayer = null; 150 } 151 } 152 Runnable postLayerTask = () -> { 153 if (!parsedProperly) { 154 String msg; 155 if (data.storageFile == null) { 156 msg = tr("Error occurred while parsing gpx data for layer ''{0}''. Only a part of the file will be available.", 157 gpxLayerName); 158 } else { 159 msg = tr("Error occurred while parsing gpx file ''{0}''. Only a part of the file will be available.", 160 data.storageFile.getPath()); 161 } 162 JOptionPane.showMessageDialog(null, msg); 163 } 164 }; 165 return new GpxImporterData(gpxLayer, markerLayer, postLayerTask); 166 } 167 168 public static GpxImporterData loadLayers(InputStream is, final File associatedFile, 169 final String gpxLayerName, String markerLayerName, ProgressMonitor progressMonitor) throws IOException { 170 try { 171 final GpxReader r = new GpxReader(is); 172 final boolean parsedProperly = r.parse(true); 173 r.getGpxData().storageFile = associatedFile; 174 return loadLayers(r.getGpxData(), parsedProperly, gpxLayerName, markerLayerName); 175 } catch (SAXException e) { 176 Main.error(e); 177 throw new IOException(tr("Parsing data for layer ''{0}'' failed", gpxLayerName), e); 178 } 179 } 180}