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 * The GPX file filter (*.gpx and *.gpx.gz files). 029 */ 030 public static final ExtensionFileFilter FILE_FILTER = ExtensionFileFilter.newFilterWithArchiveExtensions( 031 "gpx", "gpx", tr("GPX Files"), true); 032 033 /** 034 * Utility class containing imported GPX and marker layers, and a task to run after they are added to MapView. 035 */ 036 public static class GpxImporterData { 037 /** 038 * The imported GPX layer. May be null if no GPX data. 039 */ 040 private GpxLayer gpxLayer; 041 /** 042 * The imported marker layer. May be null if no marker. 043 */ 044 private MarkerLayer markerLayer; 045 /** 046 * The task to run after GPX and/or marker layer has been added to MapView. 047 */ 048 private Runnable postLayerTask; 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 public GpxLayer getGpxLayer() { 057 return gpxLayer; 058 } 059 060 public MarkerLayer getMarkerLayer() { 061 return markerLayer; 062 } 063 064 public Runnable getPostLayerTask() { 065 return postLayerTask; 066 } 067 } 068 069 /** 070 * Constructs a new {@code GpxImporter}. 071 */ 072 public GpxImporter() { 073 super(FILE_FILTER); 074 } 075 076 @Override 077 public void importData(File file, ProgressMonitor progressMonitor) throws IOException { 078 final String fileName = file.getName(); 079 080 try (InputStream is = Compression.getUncompressedFileInputStream(file)) { 081 GpxReader r = new GpxReader(is); 082 boolean parsedProperly = r.parse(true); 083 r.getGpxData().storageFile = file; 084 addLayers(loadLayers(r.getGpxData(), parsedProperly, fileName, tr("Markers from {0}", fileName))); 085 } catch (SAXException e) { 086 Main.error(e); 087 throw new IOException(tr("Parsing data for layer ''{0}'' failed", fileName), e); 088 } 089 } 090 091 /** 092 * Adds the specified GPX and marker layers to Map.main 093 * @param data The layers to add 094 * @see #loadLayers 095 */ 096 public static void addLayers(final GpxImporterData data) { 097 // FIXME: remove UI stuff from the IO subsystem 098 GuiHelper.runInEDT(new Runnable() { 099 @Override 100 public void run() { 101 if (data.markerLayer != null) { 102 Main.main.addLayer(data.markerLayer); 103 } 104 if (data.gpxLayer != null) { 105 Main.main.addLayer(data.gpxLayer); 106 } 107 data.postLayerTask.run(); 108 } 109 }); 110 } 111 112 /** 113 * Replies the new GPX and marker layers corresponding to the specified GPX data. 114 * @param data The GPX data 115 * @param parsedProperly True if GPX data has been properly parsed by {@link GpxReader#parse} 116 * @param gpxLayerName The GPX layer name 117 * @param markerLayerName The marker layer name 118 * @return the new GPX and marker layers corresponding to the specified GPX data, to be used with {@link #addLayers} 119 * @see #addLayers 120 */ 121 public static GpxImporterData loadLayers(final GpxData data, final boolean parsedProperly, 122 final String gpxLayerName, String markerLayerName) { 123 GpxLayer gpxLayer = null; 124 MarkerLayer markerLayer = null; 125 if (data.hasRoutePoints() || data.hasTrackPoints()) { 126 gpxLayer = new GpxLayer(data, gpxLayerName, data.storageFile != null); 127 } 128 if (Main.pref.getBoolean("marker.makeautomarkers", true) && !data.waypoints.isEmpty()) { 129 markerLayer = new MarkerLayer(data, markerLayerName, data.storageFile, gpxLayer); 130 if (markerLayer.data.isEmpty()) { 131 markerLayer = null; 132 } 133 } 134 Runnable postLayerTask = new Runnable() { 135 @Override 136 public void run() { 137 if (!parsedProperly) { 138 String msg; 139 if (data.storageFile == null) { 140 msg = tr("Error occurred while parsing gpx data for layer ''{0}''. Only a part of the file will be available.", 141 gpxLayerName); 142 } else { 143 msg = tr("Error occurred while parsing gpx file ''{0}''. Only a part of the file will be available.", 144 data.storageFile.getPath()); 145 } 146 JOptionPane.showMessageDialog(null, msg); 147 } 148 } 149 }; 150 return new GpxImporterData(gpxLayer, markerLayer, postLayerTask); 151 } 152 153 public static GpxImporterData loadLayers(InputStream is, final File associatedFile, 154 final String gpxLayerName, String markerLayerName, ProgressMonitor progressMonitor) throws IOException { 155 try { 156 final GpxReader r = new GpxReader(is); 157 final boolean parsedProperly = r.parse(true); 158 r.getGpxData().storageFile = associatedFile; 159 return loadLayers(r.getGpxData(), parsedProperly, gpxLayerName, markerLayerName); 160 } catch (SAXException e) { 161 Main.error(e); 162 throw new IOException(tr("Parsing data for layer ''{0}'' failed", gpxLayerName), e); 163 } 164 } 165}