Report the correct error message on invalid "when" tag
[GPXSee.git] / src / data / kmlparser.cpp
blobbbae32fa487aaac3312d27bd03f0e01c2a9a4c4b
1 /*
2 WARNING: This code uses internal Qt API - the QZipReader class for reading
3 ZIP files - and things may break if Qt changes the API. For Qt5 this is not
4 a problem as we can "see the future" now and there are no changes in all
5 the supported Qt5 versions up to the last one (5.15). In Qt6 the class
6 might change or even disappear in the future, but this is very unlikely
7 as there were no changes for several years and The Qt Company's policy
8 is: "do not invest any resources into any desktop related stuff unless
9 absolutely necessary". There is an issue (QTBUG-3897) since the year 2009 to
10 include the ZIP reader into the public API, which aptly illustrates the
11 effort The Qt Company is willing to make about anything desktop related...
14 #include <QFileInfo>
15 #include <QTemporaryDir>
16 #include <QCryptographicHash>
17 #include <QtEndian>
18 #include <QUrl>
19 #include <QRegularExpression>
20 #include <private/qzipreader_p.h>
21 #include "common/util.h"
22 #include "kmlparser.h"
24 static bool isZIP(QFile *file)
26 quint32 magic;
28 return (file->read((char *)&magic, sizeof(magic)) == (qint64)sizeof(magic)
29 && qFromLittleEndian(magic) == 0x04034b50);
32 qreal KMLParser::number()
34 bool res;
35 QString str(_reader.readElementText());
37 if (str.isEmpty())
38 return NAN;
40 qreal ret = str.toDouble(&res);
41 if (!res)
42 _reader.raiseError(QString("Invalid %1").arg(
43 _reader.name().toString()));
45 return ret;
48 QDateTime KMLParser::time()
50 QDateTime d = QDateTime::fromString(_reader.readElementText(),
51 Qt::ISODate);
52 if (!d.isValid())
53 _reader.raiseError(QString("Invalid %1").arg(
54 _reader.name().toString()));
56 return d;
59 bool KMLParser::coord(Trackpoint &trackpoint)
61 QString data = _reader.readElementText();
62 const QChar *sp, *ep, *cp, *vp;
63 int c = 0;
64 qreal val[3];
65 bool res;
67 if (data.isEmpty())
68 return true;
70 sp = data.constData();
71 ep = sp + data.size();
73 for (cp = sp; cp < ep; cp++)
74 if (!cp->isSpace())
75 break;
77 for (vp = cp; cp <= ep; cp++) {
78 if (cp->isSpace() || cp->isNull()) {
79 if (c > 2)
80 return false;
82 val[c] = QString(vp, cp - vp).toDouble(&res);
83 if (!res)
84 return false;
86 if (c == 1) {
87 trackpoint.setCoordinates(Coordinates(val[0], val[1]));
88 if (!trackpoint.coordinates().isValid())
89 return false;
90 } else if (c == 2)
91 trackpoint.setElevation(val[2]);
93 while (cp->isSpace())
94 cp++;
95 vp = cp;
96 c++;
100 return true;
103 bool KMLParser::pointCoordinates(Waypoint &waypoint)
105 QString data = _reader.readElementText();
106 const QChar *sp, *ep, *cp, *vp;
107 int c = 0;
108 qreal val[3];
109 bool res;
112 sp = data.constData();
113 ep = sp + data.size();
115 for (cp = sp; cp < ep; cp++)
116 if (!cp->isSpace())
117 break;
119 for (vp = cp; cp <= ep; cp++) {
120 if (*cp == ',') {
121 if (c > 1)
122 return false;
124 val[c] = QString(vp, cp - vp).toDouble(&res);
125 if (!res)
126 return false;
128 c++;
129 vp = cp + 1;
130 } else if (cp->isSpace() || cp->isNull()) {
131 if (c < 1)
132 return false;
134 val[c] = QString(vp, cp - vp).toDouble(&res);
135 if (!res)
136 return false;
138 waypoint.setCoordinates(Coordinates(val[0], val[1]));
139 if (!waypoint.coordinates().isValid())
140 return false;
141 if (c == 2)
142 waypoint.setElevation(val[2]);
144 while (cp->isSpace())
145 cp++;
146 c = 3;
150 return true;
153 bool KMLParser::lineCoordinates(SegmentData &segment)
155 QString data = _reader.readElementText();
156 const QChar *sp, *ep, *cp, *vp;
157 int c = 0;
158 qreal val[3];
159 bool res;
162 sp = data.constData();
163 ep = sp + data.size();
165 for (cp = sp; cp < ep; cp++)
166 if (!cp->isSpace())
167 break;
169 for (vp = cp; cp <= ep; cp++) {
170 if (*cp == ',') {
171 if (c > 1)
172 return false;
174 val[c] = QString(vp, cp - vp).toDouble(&res);
175 if (!res)
176 return false;
178 c++;
179 vp = cp + 1;
180 } else if (cp->isSpace() || cp->isNull()) {
181 if (c < 1 || c > 2)
182 return false;
184 val[c] = QString(vp, cp - vp).toDouble(&res);
185 if (!res)
186 return false;
188 segment.append(Trackpoint(Coordinates(val[0], val[1])));
189 if (!segment.last().coordinates().isValid())
190 return false;
191 if (c == 2)
192 segment.last().setElevation(val[2]);
194 while (cp->isSpace())
195 cp++;
196 c = 0;
197 vp = cp;
201 return true;
204 bool KMLParser::polygonCoordinates(QVector<Coordinates> &points)
206 QString data = _reader.readElementText();
207 const QChar *sp, *ep, *cp, *vp;
208 int c = 0;
209 qreal val[3];
210 bool res;
213 sp = data.constData();
214 ep = sp + data.size();
216 for (cp = sp; cp < ep; cp++)
217 if (!cp->isSpace())
218 break;
220 for (vp = cp; cp <= ep; cp++) {
221 if (*cp == ',') {
222 if (c > 1)
223 return false;
225 val[c] = QString(vp, cp - vp).toDouble(&res);
226 if (!res)
227 return false;
229 c++;
230 vp = cp + 1;
231 } else if (cp->isSpace() || cp->isNull()) {
232 if (c < 1 || c > 2)
233 return false;
235 val[c] = QString(vp, cp - vp).toDouble(&res);
236 if (!res)
237 return false;
239 points.append(Coordinates(val[0], val[1]));
240 if (!points.last().isValid())
241 return false;
243 while (cp->isSpace())
244 cp++;
245 c = 0;
246 vp = cp;
250 return true;
253 QDateTime KMLParser::timeStamp()
255 QDateTime ts;
257 while (_reader.readNextStartElement()) {
258 if (_reader.name() == QLatin1String("when"))
259 ts = time();
260 else
261 _reader.skipCurrentElement();
264 return ts;
267 void KMLParser::lineString(SegmentData &segment)
269 while (_reader.readNextStartElement()) {
270 if (_reader.name() == QLatin1String("coordinates")) {
271 if (!lineCoordinates(segment))
272 _reader.raiseError("Invalid coordinates");
273 } else
274 _reader.skipCurrentElement();
278 void KMLParser::linearRing(QVector<Coordinates> &coordinates)
280 while (_reader.readNextStartElement()) {
281 if (_reader.name() == QLatin1String("coordinates")) {
282 if (!polygonCoordinates(coordinates))
283 _reader.raiseError("Invalid coordinates");
284 } else
285 _reader.skipCurrentElement();
289 void KMLParser::boundary(QVector<Coordinates> &coordinates)
291 while (_reader.readNextStartElement()) {
292 if (_reader.name() == QLatin1String("LinearRing"))
293 linearRing(coordinates);
294 else
295 _reader.skipCurrentElement();
299 void KMLParser::polygon(Area &area)
301 Polygon polygon;
303 while (_reader.readNextStartElement()) {
304 QVector<Coordinates> path;
306 if (_reader.name() == QLatin1String("outerBoundaryIs")) {
307 if (!polygon.isEmpty()) {
308 _reader.raiseError("Multiple polygon outerBoundaryIss");
309 return;
311 boundary(path);
312 polygon.append(path);
313 } else if (_reader.name() == QLatin1String("innerBoundaryIs")) {
314 if (polygon.isEmpty()) {
315 _reader.raiseError("Missing polygon outerBoundaryIs");
316 return;
318 boundary(path);
319 polygon.append(path);
320 } else
321 _reader.skipCurrentElement();
324 area.append(polygon);
327 void KMLParser::point(Waypoint &waypoint)
329 while (_reader.readNextStartElement()) {
330 if (_reader.name() == QLatin1String("coordinates")) {
331 if (!pointCoordinates(waypoint))
332 _reader.raiseError("Invalid coordinates");
333 } else
334 _reader.skipCurrentElement();
337 if (waypoint.coordinates().isNull())
338 _reader.raiseError("Missing Point coordinates");
341 void KMLParser::heartRate(SegmentData &segment)
343 int i = 0;
344 const char error[] = "Heartrate data count mismatch";
346 while (_reader.readNextStartElement()) {
347 if (_reader.name() == QLatin1String("value")) {
348 if (i < segment.size())
349 segment[i++].setHeartRate(number());
350 else {
351 _reader.raiseError(error);
352 return;
354 } else
355 _reader.skipCurrentElement();
358 if (!_reader.error() && i != segment.size())
359 _reader.raiseError(error);
362 void KMLParser::cadence(SegmentData &segment)
364 int i = 0;
365 const char error[] = "Cadence data count mismatch";
367 while (_reader.readNextStartElement()) {
368 if (_reader.name() == QLatin1String("value")) {
369 if (i < segment.size())
370 segment[i++].setCadence(number());
371 else {
372 _reader.raiseError(error);
373 return;
375 } else
376 _reader.skipCurrentElement();
379 if (!_reader.error() && i != segment.size())
380 _reader.raiseError(error);
383 void KMLParser::speed(SegmentData &segment)
385 int i = 0;
386 const char error[] = "Speed data count mismatch";
388 while (_reader.readNextStartElement()) {
389 if (_reader.name() == QLatin1String("value")) {
390 if (i < segment.size())
391 segment[i++].setSpeed(number());
392 else {
393 _reader.raiseError(error);
394 return;
396 } else
397 _reader.skipCurrentElement();
400 if (!_reader.error() && i != segment.size())
401 _reader.raiseError(error);
404 void KMLParser::temperature(SegmentData &segment)
406 int i = 0;
407 const char error[] = "Temperature data count mismatch";
409 while (_reader.readNextStartElement()) {
410 if (_reader.name() == QLatin1String("value")) {
411 if (i < segment.size())
412 segment[i++].setTemperature(number());
413 else {
414 _reader.raiseError(error);
415 return;
417 } else
418 _reader.skipCurrentElement();
421 if (!_reader.error() && i != segment.size())
422 _reader.raiseError(error);
425 void KMLParser::power(SegmentData &segment)
427 int i = 0;
428 const char error[] = "Power data count mismatch";
430 while (_reader.readNextStartElement()) {
431 if (_reader.name() == QLatin1String("value")) {
432 if (i < segment.size())
433 segment[i++].setPower(number());
434 else {
435 _reader.raiseError(error);
436 return;
438 } else
439 _reader.skipCurrentElement();
442 if (!_reader.error() && i != segment.size())
443 _reader.raiseError(error);
446 void KMLParser::schemaData(SegmentData &segment)
448 while (_reader.readNextStartElement()) {
449 if (_reader.name() == QLatin1String("SimpleArrayData")) {
450 QXmlStreamAttributes attr = _reader.attributes();
451 // There are files using capitalized names in the wild!
452 QString name(attr.value("name").toString().toLower());
454 if (name == QLatin1String("heartrate"))
455 heartRate(segment);
456 else if (name == QLatin1String("cadence"))
457 cadence(segment);
458 else if (name == QLatin1String("speed"))
459 speed(segment);
460 else if (name == QLatin1String("temperature"))
461 temperature(segment);
462 else if (name == QLatin1String("power"))
463 power(segment);
464 else
465 _reader.skipCurrentElement();
466 } else
467 _reader.skipCurrentElement();
471 void KMLParser::extendedData(SegmentData &segment)
473 while (_reader.readNextStartElement()) {
474 if (_reader.name() == QLatin1String("SchemaData"))
475 schemaData(segment);
476 else
477 _reader.skipCurrentElement();
481 void KMLParser::track(SegmentData &segment)
483 const char error[] = "gx:coord/when element count mismatch";
484 int i = 0;
485 bool empty = false;
487 while (_reader.readNextStartElement()) {
488 if (_reader.name() == QLatin1String("when")) {
489 segment.append(Trackpoint());
490 segment.last().setTimestamp(time());
491 if (_reader.error())
492 return;
493 } else if (_reader.name() == QLatin1String("coord")) {
494 if (i == segment.size()) {
495 _reader.raiseError(error);
496 return;
497 } else if (!coord(segment[i])) {
498 _reader.raiseError("Invalid coordinates");
499 return;
501 if (segment.at(i).coordinates().isNull())
502 empty = true;
503 i++;
504 } else if (_reader.name() == QLatin1String("ExtendedData"))
505 extendedData(segment);
506 else
507 _reader.skipCurrentElement();
510 if (i != segment.size()) {
511 _reader.raiseError(error);
512 return;
515 /* empty (invalid) coordinates are allowed per KML specification, but
516 invalid in our data representation so get rid of the segment entries */
517 if (empty) {
518 SegmentData filtered;
519 for (int i = 0; i < segment.size(); i++)
520 if (!segment.at(i).coordinates().isNull())
521 filtered.append(segment.at(i));
522 segment = filtered;
526 void KMLParser::multiTrack(TrackData &t)
528 while (_reader.readNextStartElement()) {
529 if (_reader.name() == QLatin1String("Track")) {
530 t.append(SegmentData());
531 track(t.last());
532 } else
533 _reader.skipCurrentElement();
537 void KMLParser::photoOverlay(const Ctx &ctx, QVector<Waypoint> &waypoints,
538 PointStyleMap &pointStyles, QMap<QString, QString> &map)
540 QString img, id;
541 Waypoint w;
542 QMap<QString, PolygonStyle> unused;
543 QMap<QString, LineStyle> unused2;
544 static QRegularExpression re("\\$\\[[^\\]]+\\]");
546 while (_reader.readNextStartElement()) {
547 if (_reader.name() == QLatin1String("name"))
548 w.setName(_reader.readElementText());
549 else if (_reader.name() == QLatin1String("description"))
550 w.setDescription(_reader.readElementText());
551 else if (_reader.name() == QLatin1String("phoneNumber"))
552 w.setPhone(_reader.readElementText());
553 else if (_reader.name() == QLatin1String("address"))
554 w.setAddress(_reader.readElementText());
555 else if (_reader.name() == QLatin1String("TimeStamp"))
556 w.setTimestamp(timeStamp());
557 else if (_reader.name() == QLatin1String("Style")) {
558 style(ctx.dir, pointStyles, unused, unused2);
559 id = QString();
560 } else if (_reader.name() == QLatin1String("StyleMap"))
561 styleMap(map);
562 else if (_reader.name() == QLatin1String("Icon"))
563 img = icon();
564 else if (_reader.name() == QLatin1String("Point"))
565 point(w);
566 else if (_reader.name() == QLatin1String("styleUrl"))
567 id = styleUrl();
568 else
569 _reader.skipCurrentElement();
572 if (!w.coordinates().isNull()) {
573 PointStyleMap::iterator pit = pointStyles.find(id);
574 if (pit == pointStyles.end())
575 pit = pointStyles.find(map.value(id));
576 w.setStyle(pit == pointStyles.end()
577 ? PointStyle(QColor(0x55, 0x55, 0x55)) : *pit);
579 img.replace(re, "0");
580 if (!QUrl(img).scheme().isEmpty())
581 w.addLink(Link(img, "Photo Overlay"));
582 else if (ctx.zip && Util::tempDir().isValid()) {
583 QFileInfo fi(img);
584 QByteArray id(ctx.path.toUtf8() + img.toUtf8());
585 QString path(Util::tempDir().path() + "/" + QString("%0.%1")
586 .arg(QCryptographicHash::hash(id, QCryptographicHash::Sha1)
587 .toHex(), QString(fi.suffix())));
588 QFile::rename(ctx.dir.absoluteFilePath(img), path);
589 w.addImage(path);
590 } else if (!ctx.zip)
591 w.addImage(ctx.dir.absoluteFilePath(img));
593 waypoints.append(w);
597 void KMLParser::multiGeometry(QList<TrackData> &tracks, QList<Area> &areas,
598 QVector<Waypoint> &waypoints)
600 TrackData *tp = 0;
601 Area *ap = 0;
603 while (_reader.readNextStartElement()) {
604 if (_reader.name() == QLatin1String("Point")) {
605 waypoints.append(Waypoint());
606 Waypoint &w = waypoints.last();
607 point(w);
608 } else if (_reader.name() == QLatin1String("LineString")) {
609 if (!tp) {
610 tracks.append(TrackData());
611 tp = &tracks.last();
613 tp->append(SegmentData());
614 lineString(tp->last());
615 } else if (_reader.name() == QLatin1String("Polygon")) {
616 if (!ap) {
617 areas.append(Area());
618 ap = &areas.last();
620 polygon(*ap);
621 } else
622 _reader.skipCurrentElement();
626 void KMLParser::placemark(const Ctx &ctx, QList<TrackData> &tracks,
627 QList<Area> &areas, QVector<Waypoint> &waypoints, PointStyleMap &pointStyles,
628 PolygonStyleMap &polyStyles, LineStyleMap &lineStyles,
629 QMap<QString, QString> &map)
631 QString name, desc, phone, address, id;
632 QDateTime timestamp;
633 int trkIdx = tracks.size();
634 int wptIdx = waypoints.size();
635 int areaIdx = areas.size();
637 while (_reader.readNextStartElement()) {
638 if (_reader.name() == QLatin1String("name"))
639 name = _reader.readElementText();
640 else if (_reader.name() == QLatin1String("description"))
641 desc = _reader.readElementText();
642 else if (_reader.name() == QLatin1String("phoneNumber"))
643 phone = _reader.readElementText();
644 else if (_reader.name() == QLatin1String("address"))
645 address = _reader.readElementText();
646 else if (_reader.name() == QLatin1String("TimeStamp"))
647 timestamp = timeStamp();
648 else if (_reader.name() == QLatin1String("Style")) {
649 style(ctx.dir, pointStyles, polyStyles, lineStyles);
650 id = QString();
651 } else if (_reader.name() == QLatin1String("StyleMap"))
652 styleMap(map);
653 else if (_reader.name() == QLatin1String("MultiGeometry"))
654 multiGeometry(tracks, areas, waypoints);
655 else if (_reader.name() == QLatin1String("Point")) {
656 waypoints.append(Waypoint());
657 point(waypoints.last());
658 } else if (_reader.name() == QLatin1String("LineString")
659 || _reader.name() == QLatin1String("LinearRing")) {
660 tracks.append(TrackData());
661 tracks.last().append(SegmentData());
662 lineString(tracks.last().last());
663 } else if (_reader.name() == QLatin1String("Track")) {
664 tracks.append(TrackData());
665 tracks.last().append(SegmentData());
666 track(tracks.last().last());
667 } else if (_reader.name() == QLatin1String("MultiTrack")) {
668 tracks.append(TrackData());
669 multiTrack(tracks.last());
670 } else if (_reader.name() == QLatin1String("Polygon")) {
671 areas.append(Area());
672 polygon(areas.last());
673 } else if (_reader.name() == QLatin1String("styleUrl"))
674 id = styleUrl();
675 else
676 _reader.skipCurrentElement();
679 PointStyleMap::iterator pit = pointStyles.find(id);
680 if (pit == pointStyles.end())
681 pit = pointStyles.find(map.value(id));
682 LineStyleMap::iterator lit = lineStyles.find(id);
683 if (lit == lineStyles.end())
684 lit = lineStyles.find(map.value(id));
685 PolygonStyleMap::iterator ait = polyStyles.find(id);
686 if (ait == polyStyles.end())
687 ait = polyStyles.find(map.value(id));
689 for (int i = wptIdx; i < waypoints.size(); i++) {
690 Waypoint &w = waypoints[i];
691 w.setName(name);
692 w.setDescription(desc);
693 w.setTimestamp(timestamp);
694 w.setAddress(address);
695 w.setPhone(phone);
696 w.setStyle(pit == pointStyles.end()
697 ? PointStyle(QColor(0x55, 0x55, 0x55)) : *pit);
699 for (int i = trkIdx; i < tracks.size(); i++) {
700 TrackData &t = tracks[i];
701 t.setName(name);
702 t.setDescription(desc);
703 t.setStyle(lit == lineStyles.end()
704 ? LineStyle(QColor(0x55, 0x55, 0x55), 2, Qt::SolidLine) : *lit);
706 for (int i = areaIdx; i < areas.size(); i++) {
707 Area &a = areas[i];
708 a.setName(name);
709 a.setDescription(desc);
710 a.setStyle(ait == polyStyles.end()
711 ? PolygonStyle(QColor(0x55, 0x55, 0x55, 0x99),
712 QColor(0x55, 0x55, 0x55, 0x99), 2) : *ait);
716 QString KMLParser::icon()
718 QString path;
720 while (_reader.readNextStartElement()) {
721 if (_reader.name() == QLatin1String("href"))
722 path = _reader.readElementText();
723 else
724 _reader.skipCurrentElement();
727 return path;
730 QColor KMLParser::color()
732 QString str(_reader.readElementText());
733 if (str.size() != 8)
734 return QColor();
736 bool aok, bok, gok, rok;
737 int a = str.mid(0, 2).toInt(&aok, 16);
738 int b = str.mid(2, 2).toInt(&bok, 16);
739 int g = str.mid(4, 2).toInt(&gok, 16);
740 int r = str.mid(6, 2).toInt(&rok, 16);
742 return (aok && bok && gok && rok) ? QColor(r, g, b, a) : QColor();
745 QString KMLParser::styleUrl()
747 QString id(_reader.readElementText());
748 return (id.at(0) == '#') ? id.right(id.size() - 1) : QString();
751 void KMLParser::iconStyle(const QDir &dir, const QString &id,
752 PointStyleMap &styles)
754 QPixmap img;
755 QColor c(0x55, 0x55, 0x55);
757 while (_reader.readNextStartElement()) {
758 if (_reader.name() == QLatin1String("Icon"))
759 img = QPixmap(dir.absoluteFilePath(icon()));
760 else if (_reader.name() == QLatin1String("color"))
761 c = color();
762 else
763 _reader.skipCurrentElement();
766 styles.insert(id, PointStyle(img, c));
769 void KMLParser::polyStyle(const QString &id, PolygonStyleMap &styles)
771 QColor c(0x55, 0x55, 0x55, 0x99);
772 uint fill = 1, outline = 1;
774 while (_reader.readNextStartElement()) {
775 if (_reader.name() == QLatin1String("color"))
776 c = color();
777 else if (_reader.name() == QLatin1String("fill"))
778 fill = _reader.readElementText().toUInt();
779 else if (_reader.name() == QLatin1String("outline"))
780 outline = _reader.readElementText().toUInt();
781 else
782 _reader.skipCurrentElement();
785 styles.insert(id, PolygonStyle(fill ? c : QColor(),
786 outline ? c : QColor(), 2));
789 void KMLParser::lineStyle(const QString &id, LineStyleMap &styles)
791 QColor c(0x55, 0x55, 0x55);
792 double width = 2;
794 while (_reader.readNextStartElement()) {
795 if (_reader.name() == QLatin1String("color"))
796 c = color();
797 else if (_reader.name() == QLatin1String("width"))
798 width = _reader.readElementText().toDouble();
799 else
800 _reader.skipCurrentElement();
803 styles.insert(id, LineStyle(c, width, Qt::SolidLine));
806 void KMLParser::styleMapPair(const QString &id, QMap<QString, QString> &map)
808 QString key, url;
810 while (_reader.readNextStartElement()) {
811 if (_reader.name() == QLatin1String("key"))
812 key = _reader.readElementText();
813 else if (_reader.name() == QLatin1String("styleUrl"))
814 url = styleUrl();
815 else
816 _reader.skipCurrentElement();
819 if (key == "normal")
820 map.insert(id, url);
823 void KMLParser::styleMap(QMap<QString, QString> &map)
825 QString id = _reader.attributes().value("id").toString();
827 while (_reader.readNextStartElement()) {
828 if (_reader.name() == QLatin1String("Pair"))
829 styleMapPair(id, map);
830 else
831 _reader.skipCurrentElement();
835 void KMLParser::style(const QDir &dir, PointStyleMap &pointStyles,
836 PolygonStyleMap &polyStyles, LineStyleMap &lineStyles)
838 QString id = _reader.attributes().value("id").toString();
840 while (_reader.readNextStartElement()) {
841 if (_reader.name() == QLatin1String("IconStyle"))
842 iconStyle(dir, id, pointStyles);
843 else if (_reader.name() == QLatin1String("PolyStyle"))
844 polyStyle(id, polyStyles);
845 else if (_reader.name() == QLatin1String("LineStyle"))
846 lineStyle(id, lineStyles);
847 else
848 _reader.skipCurrentElement();
852 void KMLParser::folder(const Ctx &ctx, QList<TrackData> &tracks,
853 QList<Area> &areas, QVector<Waypoint> &waypoints,
854 PointStyleMap &pointStyles, PolygonStyleMap &polyStyles,
855 LineStyleMap &lineStyles, QMap<QString, QString> &map)
857 while (_reader.readNextStartElement()) {
858 if (_reader.name() == QLatin1String("Document"))
859 document(ctx, tracks, areas, waypoints);
860 else if (_reader.name() == QLatin1String("Folder"))
861 folder(ctx, tracks, areas, waypoints, pointStyles, polyStyles,
862 lineStyles, map);
863 else if (_reader.name() == QLatin1String("Placemark"))
864 placemark(ctx, tracks, areas, waypoints, pointStyles, polyStyles,
865 lineStyles, map);
866 else if (_reader.name() == QLatin1String("PhotoOverlay"))
867 photoOverlay(ctx, waypoints, pointStyles, map);
868 else
869 _reader.skipCurrentElement();
873 void KMLParser::document(const Ctx &ctx, QList<TrackData> &tracks,
874 QList<Area> &areas, QVector<Waypoint> &waypoints)
876 PointStyleMap pointStyles;
877 PolygonStyleMap polyStyles;
878 LineStyleMap lineStyles;
879 QMap<QString, QString> map;
881 while (_reader.readNextStartElement()) {
882 if (_reader.name() == QLatin1String("Document"))
883 document(ctx, tracks, areas, waypoints);
884 else if (_reader.name() == QLatin1String("Folder"))
885 folder(ctx, tracks, areas, waypoints, pointStyles, polyStyles,
886 lineStyles, map);
887 else if (_reader.name() == QLatin1String("Placemark"))
888 placemark(ctx, tracks, areas, waypoints, pointStyles, polyStyles,
889 lineStyles, map);
890 else if (_reader.name() == QLatin1String("PhotoOverlay"))
891 photoOverlay(ctx, waypoints, pointStyles, map);
892 else if (_reader.name() == QLatin1String("Style"))
893 style(ctx.dir, pointStyles, polyStyles, lineStyles);
894 else if (_reader.name() == QLatin1String("StyleMap"))
895 styleMap(map);
896 else
897 _reader.skipCurrentElement();
901 void KMLParser::kml(const Ctx &ctx, QList<TrackData> &tracks,
902 QList<Area> &areas, QVector<Waypoint> &waypoints)
904 PointStyleMap pointStyles;
905 PolygonStyleMap polyStyles;
906 LineStyleMap lineStyles;
907 QMap<QString, QString> map;
909 while (_reader.readNextStartElement()) {
910 if (_reader.name() == QLatin1String("Document"))
911 document(ctx, tracks, areas, waypoints);
912 else if (_reader.name() == QLatin1String("Folder"))
913 folder(ctx, tracks, areas, waypoints, pointStyles, polyStyles,
914 lineStyles, map);
915 else if (_reader.name() == QLatin1String("Placemark"))
916 placemark(ctx, tracks, areas, waypoints, pointStyles, polyStyles,
917 lineStyles, map);
918 else if (_reader.name() == QLatin1String("PhotoOverlay"))
919 photoOverlay(ctx, waypoints, pointStyles, map);
920 else
921 _reader.skipCurrentElement();
925 bool KMLParser::parse(QFile *file, QList<TrackData> &tracks,
926 QList<RouteData> &routes, QList<Area> &areas, QVector<Waypoint> &waypoints)
928 Q_UNUSED(routes);
929 QFileInfo fi(*file);
931 _reader.clear();
933 if (isZIP(file)) {
934 QZipReader zip(fi.absoluteFilePath(), QIODevice::ReadOnly);
935 QTemporaryDir tempDir;
936 if (!tempDir.isValid() || !zip.extractAll(tempDir.path()))
937 _reader.raiseError("Error extracting ZIP file");
938 else {
939 QDir zipDir(tempDir.path());
940 QFileInfoList files(zipDir.entryInfoList(QStringList("*.kml"),
941 QDir::Files));
943 if (files.isEmpty())
944 _reader.raiseError("No KML file found in ZIP file");
945 else {
946 QFile kmlFile(files.first().absoluteFilePath());
947 if (!kmlFile.open(QIODevice::ReadOnly))
948 _reader.raiseError("Error opening KML file");
949 else {
950 _reader.setDevice(&kmlFile);
952 if (_reader.readNextStartElement()) {
953 if (_reader.name() == QLatin1String("kml"))
954 kml(Ctx(fi.absoluteFilePath(), zipDir, true),
955 tracks, areas, waypoints);
956 else
957 _reader.raiseError("Not a KML file");
962 } else {
963 file->reset();
964 _reader.setDevice(file);
966 if (_reader.readNextStartElement()) {
967 if (_reader.name() == QLatin1String("kml"))
968 kml(Ctx(fi.absoluteFilePath(), fi.absoluteDir(), false), tracks,
969 areas, waypoints);
970 else
971 _reader.raiseError("Not a KML file");
975 return !_reader.error();