001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.io.importexport;
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;
011import javax.swing.SwingUtilities;
012
013import org.openstreetmap.josm.Main;
014import org.openstreetmap.josm.actions.ExtensionFileFilter;
015import org.openstreetmap.josm.gui.HelpAwareOptionPane;
016import org.openstreetmap.josm.gui.MainApplication;
017import org.openstreetmap.josm.gui.Notification;
018import org.openstreetmap.josm.gui.io.importexport.GpxImporter.GpxImporterData;
019import org.openstreetmap.josm.gui.layer.GpxLayer;
020import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
021import org.openstreetmap.josm.gui.progress.ProgressMonitor;
022import org.openstreetmap.josm.gui.util.GuiHelper;
023import org.openstreetmap.josm.io.Compression;
024import org.openstreetmap.josm.io.nmea.NmeaReader;
025import org.openstreetmap.josm.spi.preferences.Config;
026import org.xml.sax.SAXException;
027
028/**
029 * File importer allowing to import NMEA-0183 files (*.nmea/nme/nma/log/txt files).
030 * @since 1637
031 */
032public class NMEAImporter extends FileImporter {
033
034    /**
035     * The NMEA file filter (*.nmea *.nme *.nma *.log *.txt files).
036     */
037    public static final ExtensionFileFilter FILE_FILTER = ExtensionFileFilter.newFilterWithArchiveExtensions(
038            "nmea,nme,nma,log,txt", "nmea", tr("NMEA-0183 Files"), false);
039
040    /**
041     * Constructs a new {@code NMEAImporter}.
042     */
043    public NMEAImporter() {
044        super(FILE_FILTER);
045    }
046
047    @Override
048    public void importData(File file, ProgressMonitor progressMonitor) throws IOException {
049        final String fn = file.getName();
050        try (InputStream fis = Compression.getUncompressedFileInputStream(file)) {
051            final NmeaReader r = buildAndParse(fis);
052            if (r.getNumberOfCoordinates() > 0) {
053                r.getGpxData().storageFile = file;
054                final GpxLayer gpxLayer = new GpxLayer(r.getGpxData(), fn, true);
055                final File fileFinal = file;
056
057                GuiHelper.runInEDT(() -> {
058                    MainApplication.getLayerManager().addLayer(gpxLayer);
059                    if (Config.getPref().getBoolean("marker.makeautomarkers", true)) {
060                        MarkerLayer ml = new MarkerLayer(r.getGpxData(), tr("Markers from {0}", fn), fileFinal, gpxLayer);
061                        if (!ml.data.isEmpty()) {
062                            MainApplication.getLayerManager().addLayer(ml);
063                        }
064                    }
065                });
066            }
067            showNmeaInfobox(r.getNumberOfCoordinates() > 0, r);
068        }
069    }
070
071    private static void showNmeaInfobox(boolean success, NmeaReader r) {
072        final StringBuilder msg = new StringBuilder(160).append("<html>")
073           .append(tr("Coordinates imported: {0}", r.getNumberOfCoordinates())).append("<br>")
074           .append(tr("Malformed sentences: {0}", r.getParserMalformed())).append("<br>")
075           .append(tr("Checksum errors: {0}", r.getParserChecksumErrors())).append("<br>");
076        if (!success) {
077            msg.append(tr("Unknown sentences: {0}", r.getParserUnknown())).append("<br>");
078        }
079        msg.append(tr("Zero coordinates: {0}", r.getParserZeroCoordinates()))
080           .append("</html>");
081        if (success) {
082            SwingUtilities.invokeLater(() -> new Notification(
083                    "<h3>" + tr("NMEA import success:") + "</h3>" + msg.toString())
084                    .setIcon(JOptionPane.INFORMATION_MESSAGE)
085                    .show());
086        } else {
087            HelpAwareOptionPane.showMessageDialogInEDT(
088                    Main.parent,
089                    msg.toString(),
090                    tr("NMEA import failure!"),
091                    JOptionPane.ERROR_MESSAGE, null);
092        }
093    }
094
095    /**
096     * Replies the new GPX and marker layers corresponding to the specified NMEA file.
097     * @param is input stream to NMEA 0183 data
098     * @param associatedFile NMEA file
099     * @param gpxLayerName The GPX layer name
100     * @param markerLayerName The marker layer name
101     * @return the new GPX and marker layers corresponding to the specified NMEA file
102     * @throws IOException if an I/O error occurs
103     */
104    public static GpxImporterData loadLayers(InputStream is, final File associatedFile,
105            final String gpxLayerName, String markerLayerName) throws IOException {
106        final NmeaReader r = buildAndParse(is);
107        final boolean parsedProperly = r.getNumberOfCoordinates() > 0;
108        r.getGpxData().storageFile = associatedFile;
109        return GpxImporter.loadLayers(r.getGpxData(), parsedProperly, gpxLayerName, markerLayerName);
110    }
111
112    static NmeaReader buildAndParse(InputStream fis) throws IOException {
113        final NmeaReader r = new NmeaReader(fis);
114        try {
115            r.parse(true);
116        } catch (SAXException e) {
117            throw new IOException(e);
118        }
119        return r;
120    }
121}