Marble

KmlCoordinatesTagHandler.cpp
1/*
2 SPDX-FileCopyrightText: 2008 Patrick Spendrin <ps_ml@gmx.de>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "KmlCoordinatesTagHandler.h"
8
9#include <QRegularExpression>
10#include <QStringList>
11
12#include "GeoDataLatLonQuad.h"
13#include "GeoDataLineString.h"
14#include "GeoDataLinearRing.h"
15#include "GeoDataModel.h"
16#include "GeoDataMultiGeometry.h"
17#include "GeoDataPlacemark.h"
18#include "GeoDataPoint.h"
19#include "GeoDataTrack.h"
20#include "GeoParser.h"
21#include "KmlElementDictionary.h"
22#include "MarbleDebug.h"
23#include "MarbleGlobal.h"
24
25namespace Marble
26{
27namespace kml
28{
29KML_DEFINE_TAG_HANDLER(coordinates)
30
31static const bool kmlStrictSpecs = false;
32
33// We can't use KML_DEFINE_TAG_HANDLER_GX22 because the name of the tag ("coord")
34// and the TagHandler ("KmlcoordinatesTagHandler") don't match
35static GeoTagHandlerRegistrar s_handlercoordkmlTag_nameSpaceGx22(GeoParser::QualifiedName(QLatin1StringView(kmlTag_coord),
36 QLatin1StringView(kmlTag_nameSpaceGx22)),
37 new KmlcoordinatesTagHandler());
38
39GeoNode *KmlcoordinatesTagHandler::parse(GeoParser &parser) const
40{
41 Q_ASSERT(parser.isStartElement()
42 && (parser.isValidElement(QLatin1StringView(kmlTag_coordinates)) || parser.isValidElement(QLatin1StringView(kmlTag_coord))));
43
44 GeoStackItem parentItem = parser.parentElement();
45
46 if (parentItem.represents(kmlTag_Point) || parentItem.represents(kmlTag_LineString) || parentItem.represents(kmlTag_MultiGeometry)
47 || parentItem.represents(kmlTag_LinearRing) || parentItem.represents(kmlTag_LatLonQuad)) {
48 QStringList coordinatesLines; // = parser.readElementText().trimmed().split( QRegExp("\\s"), Qt::SkipEmptyParts );
49 // Splitting using the "\\s" regexp is slow, split manually instead.
50 QString text = parser.readElementText().trimmed();
51
52 if (!kmlStrictSpecs) {
53 // Removing spaces before and after commas
54 for (int i = 1; i < text.size() - 1; ++i) {
55 if (text[i] == QLatin1Char(',')) {
56 // Before
57 int l = i - 1;
58 while (l > 0 && text[l].isSpace()) {
59 --l;
60 }
61
62 // After
63 int r = i + 1;
64 while (r < text.size() && text[r].isSpace()) {
65 ++r;
66 }
67
68 text.remove(l + 1, r - l - 1).insert(l + 1, QLatin1Char(','));
69 }
70 }
71 }
72
73 int index = 0;
74 bool inside = true;
75 int const size = text.size();
76 for (int i = 0; i < size; ++i) {
77 if (text[i].isSpace()) {
78 if (inside) {
79 coordinatesLines.append(text.mid(index, i - index));
80 inside = false;
81 }
82 index = i + 1;
83 } else {
84 inside = true;
85 }
86 }
87 coordinatesLines.append(text.mid(index));
88 int coordinatesIndex = 0;
89 for (const QString &line : coordinatesLines) {
90 const QStringList coordinates = line.trimmed().split(QLatin1Char(','));
91 if (parentItem.represents(kmlTag_Point) && parentItem.is<GeoDataFeature>()) {
92 GeoDataCoordinates coord;
93 if (coordinates.size() == 2) {
94 coord.set(coordinates.at(0).toDouble(), coordinates.at(1).toDouble(), 0.0, GeoDataCoordinates::Degree);
95 } else if (coordinates.size() == 3) {
96 coord.set(coordinates.at(0).toDouble(), coordinates.at(1).toDouble(), coordinates.at(2).toDouble(), GeoDataCoordinates::Degree);
97 }
98 parentItem.nodeAs<GeoDataPlacemark>()->setCoordinate(coord);
99 } else {
100 GeoDataCoordinates coord;
101 if (coordinates.size() == 2) {
102 coord.set(DEG2RAD * coordinates.at(0).toDouble(), DEG2RAD * coordinates.at(1).toDouble());
103 } else if (coordinates.size() == 3) {
104 coord.set(DEG2RAD * coordinates.at(0).toDouble(), DEG2RAD * coordinates.at(1).toDouble(), coordinates.at(2).toDouble());
105 }
106
107 if (parentItem.represents(kmlTag_LineString)) {
108 parentItem.nodeAs<GeoDataLineString>()->append(coord);
109 } else if (parentItem.represents(kmlTag_LinearRing)) {
110 parentItem.nodeAs<GeoDataLinearRing>()->append(coord);
111 } else if (parentItem.represents(kmlTag_MultiGeometry)) {
112 auto point = new GeoDataPoint(coord);
113 parentItem.nodeAs<GeoDataMultiGeometry>()->append(point);
114 } else if (parentItem.represents(kmlTag_Model)) {
115 parentItem.nodeAs<GeoDataModel>()->setCoordinates(coord);
116 } else if (parentItem.represents(kmlTag_Point)) {
117 // photo overlay
118 parentItem.nodeAs<GeoDataPoint>()->setCoordinates(coord);
119 } else if (parentItem.represents(kmlTag_LatLonQuad)) {
120 switch (coordinatesIndex) {
121 case 0:
122 parentItem.nodeAs<GeoDataLatLonQuad>()->setBottomLeft(coord);
123 break;
124 case 1:
125 parentItem.nodeAs<GeoDataLatLonQuad>()->setBottomRight(coord);
126 break;
127 case 2:
128 parentItem.nodeAs<GeoDataLatLonQuad>()->setTopRight(coord);
129 break;
130 case 3:
131 parentItem.nodeAs<GeoDataLatLonQuad>()->setTopLeft(coord);
132 break;
133 case 4:
134 mDebug() << "Ignoring excessive coordinates in LatLonQuad (must not have more than 4 pairs)";
135 break;
136 default:
137 // Silently ignore any more coordinates
138 break;
139 }
140 } else {
141 // raise warning as coordinates out of valid parents found
142 }
143 }
144
145 ++coordinatesIndex;
146 }
147 }
148
149 if (parentItem.represents(kmlTag_Track)) {
150 QString input = parser.readElementText().trimmed();
151 if (!kmlStrictSpecs) {
152 input.replace(QRegularExpression(QStringLiteral("\\s*,\\s*")), QStringLiteral(","));
153 }
154 const QStringList coordinates = input.split(QLatin1Char(' '));
155
156 GeoDataCoordinates coord;
157 if (coordinates.size() == 2) {
158 coord.set(DEG2RAD * coordinates.at(0).toDouble(), DEG2RAD * coordinates.at(1).toDouble());
159 } else if (coordinates.size() == 3) {
160 coord.set(DEG2RAD * coordinates.at(0).toDouble(), DEG2RAD * coordinates.at(1).toDouble(), coordinates.at(2).toDouble());
161 }
162 parentItem.nodeAs<GeoDataTrack>()->appendCoordinates(coord);
163 }
164
165 return nullptr;
166}
167
168}
169}
Binds a QML item to a specific geodetic location in screen coordinates.
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
qsizetype size() const const
QString & insert(qsizetype position, QChar ch)
QString mid(qsizetype position, qsizetype n) const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
qsizetype size() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString trimmed() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:04:14 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.