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