Fixed error line reporting in CSV-based files
[GPXSee.git] / src / data / igcparser.cpp
blob63e965d3c511f60458fa96ecc908f8df5129c637
1 #include <cstring>
2 #include "common/util.h"
3 #include "igcparser.h"
6 static bool readLat(const char *data, qreal &lat)
8 int d = Util::str2int(data, 2);
9 int mi = Util::str2int(data + 2, 2);
10 int mf = Util::str2int(data + 4, 3);
11 if (d < 0 || mi < 0 || mf < 0)
12 return false;
14 if (!(data[7] == 'N' || data[7] == 'S'))
15 return false;
17 lat = d + (((qreal)mi + (qreal)mf/1000) / 60);
18 if (lat > 90)
19 return false;
21 if (data[7] == 'S')
22 lat = -lat;
24 return true;
27 static bool readLon(const char *data, qreal &lon)
29 int d = Util::str2int(data, 3);
30 int mi = Util::str2int(data + 3, 2);
31 int mf = Util::str2int(data + 5, 3);
32 if (d < 0 || mi < 0 || mf < 0)
33 return false;
35 if (!(data[8] == 'E' || data[8] == 'W'))
36 return false;
38 lon = d + (((qreal)mi + (qreal)mf/1000) / 60);
39 if (lon > 180)
40 return false;
42 if (data[8] == 'W')
43 lon = -lon;
45 return true;
48 static bool readAltitude(const char *data, qreal &ele)
50 int ga;
52 if (!(data[0] == 'A' || data[0] == 'V'))
53 return false;
55 if (data[6] == '-') {
56 if ((ga = Util::str2int(data + 7, 4)) < 0)
57 return false;
58 ga = -ga;
59 } else {
60 if ((ga = Util::str2int(data + 6, 5)) < 0)
61 return false;
64 if (data[0] == 'A')
65 ele = (qreal)ga;
66 else
67 ele = NAN;
69 return true;
72 static bool readTimestamp(const char *data, QTime &time)
74 int h = Util::str2int(data, 2);
75 int m = Util::str2int(data + 2, 2);
76 int s = Util::str2int(data + 4, 2);
78 if (h < 0 || m < 0 || s < 0)
79 return false;
81 time = QTime(h, m, s);
82 if (!time.isValid())
83 return false;
85 return true;
88 static bool readARecord(const char *line, qint64 len)
90 /* The minimal A record length should be 7 according to the specification,
91 but records with length of 6 exist in the wild */
92 if (len < 6 || line[0] != 'A')
93 return false;
95 for (int i = 1; i < 6; i++)
96 if (!::isprint(line[i]))
97 return false;
98 return true;
101 bool IGCParser::readHRecord(CTX &ctx, const char *line, int len)
103 if (len < 11 || ::strncmp(line, "HFDTE", 5))
104 return true;
106 int offset = (len < 16 || ::strncmp(line + 5, "DATE:", 5)) ? 5 : 10;
108 int d = Util::str2int(line + offset, 2);
109 int m = Util::str2int(line + offset + 2, 2);
110 int y = Util::str2int(line + offset + 4, 2);
112 if (y < 0 || m < 0 || d < 0) {
113 _errorString = "Invalid date header format";
114 return false;
117 ctx.date = QDate(y + 2000 <= QDate::currentDate().year()
118 ? 2000 + y : 1900 + y, m, d);
119 if (!ctx.date.isValid()) {
120 _errorString = "Invalid date";
121 return false;
124 return true;
127 bool IGCParser::readBRecord(CTX &ctx, const char *line, int len,
128 SegmentData &segment)
130 qreal lat, lon, ele;
131 QTime time;
133 if (len < 35)
134 return false;
135 if (line[24] != 'A')
136 return true;
138 if (!readTimestamp(line + 1, time)) {
139 _errorString = "Invalid timestamp";
140 return false;
143 if (!readLat(line + 7, lat)) {
144 _errorString = "Invalid latitude";
145 return false;
147 if (!readLon(line + 15, lon)) {
148 _errorString = "Invalid longitude";
149 return false;
152 if (!readAltitude(line + 24, ele)) {
153 _errorString = "Invalid altitude";
154 return false;
157 if (time < ctx.time && !segment.isEmpty()
158 && ctx.date == segment.last().timestamp().date())
159 ctx.date = ctx.date.addDays(1);
160 ctx.time = time;
162 Trackpoint t(Coordinates(lon, lat));
163 t.setTimestamp(QDateTime(ctx.date, ctx.time, Qt::UTC));
164 t.setElevation(ele);
165 segment.append(t);
167 return true;
170 bool IGCParser::readCRecord(const char *line, int len, RouteData &route)
172 qreal lat, lon;
175 if (len < 18)
176 return false;
178 if (!readLat(line + 1, lat)) {
179 _errorString = "Invalid latitude";
180 return false;
182 if (!readLon(line + 9, lon)) {
183 _errorString = "Invalid longitude";
184 return false;
187 if (!(lat == 0 && lon == 0)) {
188 QByteArray ba(line + 18, len - 19);
190 Waypoint w(Coordinates(lon, lat));
191 w.setName(QString(ba.trimmed()));
192 route.append(w);
195 return true;
198 bool IGCParser::parse(QFile *file, QList<TrackData> &tracks,
199 QList<RouteData> &routes, QList<Area> &polygons,
200 QVector<Waypoint> &waypoints)
202 Q_UNUSED(waypoints);
203 Q_UNUSED(polygons);
204 qint64 len;
205 char line[76 + 2 + 1 + 1];
206 bool route = false, track = false;
207 CTX ctx;
210 _errorLine = 1;
211 _errorString.clear();
213 while (!file->atEnd()) {
214 len = file->readLine(line, sizeof(line));
216 if (len < 0) {
217 _errorString = "I/O error";
218 return false;
219 } else if (len > (qint64)sizeof(line) - 1) {
220 _errorString = "Line limit exceeded";
221 return false;
224 if (_errorLine == 1) {
225 if (!readARecord(line, len)) {
226 _errorString = "Invalid/missing A record";
227 return false;
229 } else {
230 if (line[0] == 'H') {
231 if (!readHRecord(ctx, line, len))
232 return false;
233 } else if (line[0] == 'C') {
234 if (route) {
235 if (!readCRecord(line, len, routes.last()))
236 return false;
237 } else {
238 route = true;
239 routes.append(RouteData());
241 } else if (line[0] == 'B') {
242 if (ctx.date.isNull()) {
243 /* The date H header is mandatory, but XCSOAR generates
244 files without it, so add a dummy date in such case */
245 qWarning("%s: Missing date header",
246 qPrintable(file->fileName()));
247 ctx.date = QDate(1970, 1, 1);
249 if (!track) {
250 tracks.append(SegmentData());
251 ctx.time = QTime(0, 0);
252 track = true;
254 if (!readBRecord(ctx, line, len, tracks.last().last()))
255 return false;
259 _errorLine++;
262 return true;