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