001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.io.rtklib; 003 004import java.io.BufferedReader; 005import java.io.IOException; 006import java.io.InputStream; 007import java.io.InputStreamReader; 008import java.nio.charset.StandardCharsets; 009import java.util.ArrayList; 010import java.util.Collection; 011import java.util.Collections; 012import java.util.Objects; 013 014import org.openstreetmap.josm.data.coor.LatLon; 015import org.openstreetmap.josm.data.gpx.GpxConstants; 016import org.openstreetmap.josm.data.gpx.GpxData; 017import org.openstreetmap.josm.data.gpx.GpxTrack; 018import org.openstreetmap.josm.data.gpx.WayPoint; 019import org.openstreetmap.josm.io.IGpxReader; 020import org.openstreetmap.josm.tools.Logging; 021import org.openstreetmap.josm.tools.date.DateUtils; 022import org.xml.sax.SAXException; 023 024/** 025 * Reads a RTKLib Positioning Solution file. 026 * <p> 027 * See <a href="https://github.com/tomojitakasu/RTKLIB/blob/rtklib_2.4.3/doc/manual_2.4.2.pdf">RTKLIB Manual</a>. 028 * @since 15247 029 */ 030public class RtkLibPosReader implements IGpxReader { 031 032 private static final int IDX_DATE = 0; 033 private static final int IDX_TIME = 1; 034 private static final int IDX_LAT = 2; 035 private static final int IDX_LON = 3; 036 private static final int IDX_HEIGHT = 4; 037 private static final int IDX_Q = 5; 038 private static final int IDX_NS = 6; 039 private static final int IDX_SDN = 7; 040 private static final int IDX_SDE = 8; 041 private static final int IDX_SDU = 9; 042 private static final int IDX_SDNE = 10; 043 private static final int IDX_SDEU = 11; 044 private static final int IDX_SDUN = 12; 045 private static final int IDX_AGE = 13; 046 private static final int IDX_RATIO = 14; 047 048 private final InputStream source; 049 private GpxData data; 050 private int success; // number of successfully parsed lines 051 052 /** 053 * Constructs a new {@code RtkLibPosReader} 054 * @param source RTKLib .pos file input stream 055 * @throws IOException if an I/O error occurs 056 */ 057 public RtkLibPosReader(InputStream source) throws IOException { 058 this.source = Objects.requireNonNull(source); 059 } 060 061 @Override 062 public boolean parse(boolean tryToFinish) throws SAXException, IOException { 063 data = new GpxData(); 064 Collection<Collection<WayPoint>> currentTrack = new ArrayList<>(); 065 Collection<WayPoint> waypoints = new ArrayList<>(); 066 try (BufferedReader rd = new BufferedReader(new InputStreamReader(source, StandardCharsets.UTF_8))) { 067 String line; 068 do { 069 line = rd.readLine(); 070 if (line != null) { 071 if (line.startsWith("% ref pos :")) { 072 // TODO add marker 073 } else if (!line.startsWith("%")) { 074 try { 075 String[] fields = line.split("[ ]+", -1); 076 WayPoint currentwp = new WayPoint(new LatLon( 077 Double.parseDouble(fields[IDX_LAT]), 078 Double.parseDouble(fields[IDX_LON]))); 079 currentwp.put(GpxConstants.PT_ELE, fields[IDX_HEIGHT]); 080 currentwp.setInstant(DateUtils.parseInstant(fields[IDX_DATE]+" "+fields[IDX_TIME])); 081 currentwp.put(GpxConstants.RTKLIB_Q, Integer.parseInt(fields[IDX_Q])); 082 currentwp.put(GpxConstants.PT_SAT, fields[IDX_NS]); 083 currentwp.put(GpxConstants.RTKLIB_SDN, fields[IDX_SDN]); 084 currentwp.put(GpxConstants.RTKLIB_SDE, fields[IDX_SDE]); 085 currentwp.put(GpxConstants.RTKLIB_SDU, fields[IDX_SDU]); 086 currentwp.put(GpxConstants.RTKLIB_SDNE, fields[IDX_SDNE]); 087 currentwp.put(GpxConstants.RTKLIB_SDEU, fields[IDX_SDEU]); 088 currentwp.put(GpxConstants.RTKLIB_SDUN, fields[IDX_SDUN]); 089 currentwp.put(GpxConstants.RTKLIB_AGE, fields[IDX_AGE]); 090 currentwp.put(GpxConstants.RTKLIB_RATIO, fields[IDX_RATIO]); 091 double sdn = Double.parseDouble(fields[IDX_SDN]); 092 double sde = Double.parseDouble(fields[IDX_SDE]); 093 currentwp.put(GpxConstants.PT_HDOP, (float) Math.sqrt(sdn*sdn + sde*sde)); 094 waypoints.add(currentwp); 095 success++; 096 } catch (IllegalArgumentException e) { 097 Logging.error(e); 098 } 099 } 100 } 101 } while (line != null); 102 } 103 currentTrack.add(waypoints); 104 data.tracks.add(new GpxTrack(currentTrack, Collections.<String, Object>emptyMap())); 105 return true; 106 } 107 108 @Override 109 public GpxData getGpxData() { 110 return data; 111 } 112 113 @Override 114 public int getNumberOfCoordinates() { 115 return success; 116 } 117}