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(new Runnable() { 120 @Override 121 public void run() { 122 if (data.markerLayer != null) { 123 Main.main.addLayer(data.markerLayer); 124 } 125 if (data.gpxLayer != null) { 126 Main.main.addLayer(data.gpxLayer); 127 } 128 data.postLayerTask.run(); 129 } 130 }); 131 } 132 133 /** 134 * Replies the new GPX and marker layers corresponding to the specified GPX data. 135 * @param data The GPX data 136 * @param parsedProperly True if GPX data has been properly parsed by {@link GpxReader#parse} 137 * @param gpxLayerName The GPX layer name 138 * @param markerLayerName The marker layer name 139 * @return the new GPX and marker layers corresponding to the specified GPX data, to be used with {@link #addLayers} 140 * @see #addLayers 141 */ 142 public static GpxImporterData loadLayers(final GpxData data, final boolean parsedProperly, 143 final String gpxLayerName, String markerLayerName) { 144 GpxLayer gpxLayer = null; 145 MarkerLayer markerLayer = null; 146 if (data.hasRoutePoints() || data.hasTrackPoints()) { 147 gpxLayer = new GpxLayer(data, gpxLayerName, data.storageFile != null); 148 } 149 if (Main.pref.getBoolean("marker.makeautomarkers", true) && !data.waypoints.isEmpty()) { 150 markerLayer = new MarkerLayer(data, markerLayerName, data.storageFile, gpxLayer); 151 if (markerLayer.data.isEmpty()) { 152 markerLayer = null; 153 } 154 } 155 Runnable postLayerTask = new Runnable() { 156 @Override 157 public void run() { 158 if (!parsedProperly) { 159 String msg; 160 if (data.storageFile == null) { 161 msg = tr("Error occurred while parsing gpx data for layer ''{0}''. Only a part of the file will be available.", 162 gpxLayerName); 163 } else { 164 msg = tr("Error occurred while parsing gpx file ''{0}''. Only a part of the file will be available.", 165 data.storageFile.getPath()); 166 } 167 JOptionPane.showMessageDialog(null, msg); 168 } 169 } 170 }; 171 return new GpxImporterData(gpxLayer, markerLayer, postLayerTask); 172 } 173 174 public static GpxImporterData loadLayers(InputStream is, final File associatedFile, 175 final String gpxLayerName, String markerLayerName, ProgressMonitor progressMonitor) throws IOException { 176 try { 177 final GpxReader r = new GpxReader(is); 178 final boolean parsedProperly = r.parse(true); 179 r.getGpxData().storageFile = associatedFile; 180 return loadLayers(r.getGpxData(), parsedProperly, gpxLayerName, markerLayerName); 181 } catch (SAXException e) { 182 Main.error(e); 183 throw new IOException(tr("Parsing data for layer ''{0}'' failed", gpxLayerName), e); 184 } 185 } 186}