001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.io;
003    
004    import static org.openstreetmap.josm.tools.I18n.tr;
005    
006    import java.io.File;
007    import java.io.FileInputStream;
008    import java.io.IOException;
009    import java.io.InputStream;
010    import java.util.zip.GZIPInputStream;
011    
012    import javax.swing.JOptionPane;
013    
014    import org.openstreetmap.josm.Main;
015    import org.openstreetmap.josm.actions.ExtensionFileFilter;
016    import org.openstreetmap.josm.data.gpx.GpxData;
017    import org.openstreetmap.josm.gui.layer.GpxLayer;
018    import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
019    import org.openstreetmap.josm.gui.progress.ProgressMonitor;
020    import org.openstreetmap.josm.gui.util.GuiHelper;
021    import org.xml.sax.SAXException;
022    
023    /**
024     * File importer allowing to import GPX files (*.gpx/gpx.gz files).
025     *
026     */
027    public 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.data.storageFile = file;
092                addLayers(loadLayers(r.data, 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                public void run() {
108                    if (data.markerLayer != null) {
109                        Main.main.addLayer(data.markerLayer);
110                    }
111                    if (data.gpxLayer != null) {
112                        Main.main.addLayer(data.gpxLayer);
113                    }
114                    data.postLayerTask.run();
115                }
116            });
117        }
118    
119        /**
120         * Replies the new GPX and marker layers corresponding to the specified GPX data.
121         * @param data The GPX data
122         * @param parsedProperly True if GPX data has been properly parsed by {@link GpxReader#parse}
123         * @param gpxLayerName The GPX layer name
124         * @param markerLayerName The marker layer name
125         * @return the new GPX and marker layers corresponding to the specified GPX data, to be used with {@link #addLayers}
126         * @see #addLayers
127         */
128        public static GpxImporterData loadLayers(final GpxData data, final boolean parsedProperly, 
129                final String gpxLayerName, String markerLayerName) {
130            GpxLayer gpxLayer = null;
131            MarkerLayer markerLayer = null;
132            if (data.hasRoutePoints() || data.hasTrackPoints()) {
133                gpxLayer = new GpxLayer(data, gpxLayerName, data.storageFile != null);
134            }
135            if (Main.pref.getBoolean("marker.makeautomarkers", true) && !data.waypoints.isEmpty()) {
136                markerLayer = new MarkerLayer(data, markerLayerName, data.storageFile, gpxLayer);
137                if (markerLayer.data.isEmpty()) {
138                    markerLayer = null;
139                }
140            }
141            Runnable postLayerTask = new Runnable() {
142                @Override
143                public void run() {
144                    if (!parsedProperly) {
145                        String msg;
146                        if (data.storageFile == null) {
147                            msg = tr("Error occurred while parsing gpx data for layer ''{0}''. Only a part of the file will be available.",
148                                    gpxLayerName);
149                        } else {
150                            msg = tr("Error occurred while parsing gpx file ''{0}''. Only a part of the file will be available.",
151                                    data.storageFile.getPath());
152                        }
153                        JOptionPane.showMessageDialog(null, msg);
154                    }
155                }
156            };
157            return new GpxImporterData(gpxLayer, markerLayer, postLayerTask);
158        }
159    
160        public static GpxImporterData loadLayers(InputStream is, final File associatedFile,
161                final String gpxLayerName, String markerLayerName, ProgressMonitor progressMonitor) throws IOException {
162            try {
163                final GpxReader r = new GpxReader(is);
164                final boolean parsedProperly = r.parse(true);
165                r.data.storageFile = associatedFile;
166                return loadLayers(r.data, parsedProperly, gpxLayerName, markerLayerName);
167            } catch (SAXException e) {
168                e.printStackTrace();
169                throw new IOException(tr("Parsing data for layer ''{0}'' failed", gpxLayerName));
170            }
171        }
172    }