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}