29 errorString = QCoreApplication::translate(
"KMLHelper", _errorPrefix).arg(QCoreApplication::translate(
"KML",
"File not found: %1").arg(kmlFile));
30 return QDomDocument();
33 if (!file.open(QIODevice::ReadOnly)) {
34 errorString = QCoreApplication::translate(
"KMLHelper", _errorPrefix).arg(QCoreApplication::translate(
"KML",
"Unable to open file: %1 error: %2").arg(kmlFile, file.errorString()));
35 return QDomDocument();
39 const QDomDocument::ParseResult result = doc.setContent(&file, QDomDocument::ParseOption::Default);
41 errorString = QCoreApplication::translate(
"KMLHelper", _errorPrefix).arg(QCoreApplication::translate(
"KML",
"Unable to parse KML file: %1 error: %2 line: %3").arg(kmlFile).arg(result.errorMessage).arg(result.errorLine));
42 return QDomDocument();
51 const QString simplified = coordinatesString.simplified();
52 if (simplified.isEmpty()) {
53 errorString = QCoreApplication::translate(
"KMLHelper", _errorPrefix).arg(QCoreApplication::translate(
"KML",
"Empty coordinates string"));
57 const QStringList rgCoordinateStrings = simplified.split(
' ');
58 for (
const QString &coordinateString : rgCoordinateStrings) {
59 if (coordinateString.isEmpty()) {
62 const QStringList rgValueStrings = coordinateString.split(
',');
63 if (rgValueStrings.size() < 2) {
64 qCWarning(KMLHelperLog) <<
"Invalid coordinate format, expected lon,lat[,alt]:" << coordinateString;
67 bool lonOk =
false, latOk =
false;
68 const double lon = rgValueStrings[0].toDouble(&lonOk);
69 const double lat = rgValueStrings[1].toDouble(&latOk);
70 if (!lonOk || !latOk) {
71 qCWarning(KMLHelperLog) <<
"Failed to parse coordinate values:" << coordinateString;
74 if (lat < -90.0 || lat > 90.0) {
75 qCWarning(KMLHelperLog) <<
"Latitude out of range [-90, 90]:" << lat <<
"in:" << coordinateString;
78 if (lon < -180.0 || lon > 180.0) {
79 qCWarning(KMLHelperLog) <<
"Longitude out of range [-180, 180]:" << lon <<
"in:" << coordinateString;
83 if (rgValueStrings.size() >= 3) {
84 alt = rgValueStrings[2].toDouble();
86 coords.append(QGeoCoordinate(lat, lon, alt));
89 if (coords.isEmpty()) {
90 errorString = QCoreApplication::translate(
"KMLHelper", _errorPrefix).arg(QCoreApplication::translate(
"KML",
"No valid coordinates found"));
98 if (filterMeters <= 0 || vertices.count() <= minVertices) {
103 while (i < (vertices.count() - 1)) {
104 if ((vertices.count() > minVertices) && (vertices[i].distanceTo(vertices[i + 1]) < filterMeters)) {
105 vertices.removeAt(i + 1);
116 const QDomNode altModeNode = geometryNode.namedItem(
"altitudeMode");
117 if (!altModeNode.isNull()) {
118 const QString mode = altModeNode.toElement().text();
119 if (mode.isEmpty()) {
123 const QString location = QStringLiteral(
"(line %1)").arg(altModeNode.lineNumber());
124 if (!validator->isValidEnumValue(
"altitudeModeEnumType", mode)) {
125 qCWarning(KMLHelperLog) << geometryType << index << location <<
"has invalid altitudeMode:" << mode
126 <<
"- valid values are:" << validator->validEnumValues(
"altitudeModeEnumType").join(
", ");
127 }
else if (mode !=
"absolute") {
128 qCWarning(KMLHelperLog) << geometryType << index << location <<
"uses altitudeMode:" << mode
129 <<
"- QGC will treat coordinates as absolute (AMSL)";
140 return ShapeType::Error;
143 const QDomNodeList rgNodesPolygon = domDocument.elementsByTagName(
"Polygon");
144 if (!rgNodesPolygon.isEmpty()) {
145 return ShapeType::Polygon;
148 const QDomNodeList rgNodesLineString = domDocument.elementsByTagName(
"LineString");
149 if (!rgNodesLineString.isEmpty()) {
150 return ShapeType::Polyline;
153 const QDomNodeList rgNodesPoint = domDocument.elementsByTagName(
"Point");
154 if (!rgNodesPoint.isEmpty()) {
155 return ShapeType::Point;
158 errorString = QCoreApplication::translate(
"KMLHelper", _errorPrefix).arg(QCoreApplication::translate(
"KML",
"No supported type found in KML file."));
159 return ShapeType::Error;
186 const QDomNodeList rgNodes = domDocument.elementsByTagName(
"Polygon");
187 if (rgNodes.isEmpty()) {
188 errorString = QCoreApplication::translate(
"KMLHelper", _errorPrefix).arg(QCoreApplication::translate(
"KML",
"Unable to find Polygon node in KML"));
192 for (
int nodeIdx = 0; nodeIdx < rgNodes.count(); nodeIdx++) {
193 const QDomNode polygonNode = rgNodes.item(nodeIdx);
196 const QDomNode coordinatesNode = polygonNode.namedItem(
"outerBoundaryIs").namedItem(
"LinearRing").namedItem(
"coordinates");
197 if (coordinatesNode.isNull()) {
198 qCWarning(KMLHelperLog) <<
"Polygon" << nodeIdx << QStringLiteral(
"(line %1)").arg(polygonNode.lineNumber())
199 <<
"missing coordinates node, skipping";
203 QList<QGeoCoordinate> rgCoords;
205 qCWarning(KMLHelperLog) <<
"Polygon" << nodeIdx << QStringLiteral(
"(line %1)").arg(coordinatesNode.lineNumber())
211 if (rgCoords.count() < 3) {
212 qCWarning(KMLHelperLog) <<
"Polygon" << nodeIdx << QStringLiteral(
"(line %1)").arg(polygonNode.lineNumber())
213 <<
"has fewer than 3 vertices, skipping";
218 if (rgCoords.count() > 3 && rgCoords.first().latitude() == rgCoords.last().latitude() &&
219 rgCoords.first().longitude() == rgCoords.last().longitude()) {
220 rgCoords.removeLast();
225 for (
int i = 0; i < rgCoords.count(); i++) {
226 const QGeoCoordinate &coord1 = rgCoords[i];
227 const QGeoCoordinate &coord2 = rgCoords[(i + 1) % rgCoords.count()];
228 sum += (coord2.longitude() - coord1.longitude()) * (coord2.latitude() + coord1.latitude());
231 std::reverse(rgCoords.begin(), rgCoords.end());
235 polygons.append(rgCoords);
238 if (polygons.isEmpty()) {
239 errorString = QCoreApplication::translate(
"KMLHelper", _errorPrefix).arg(QCoreApplication::translate(
"KML",
"No valid polygons found in KML file"));
256 const QDomNodeList rgNodes = domDocument.elementsByTagName(
"LineString");
257 if (rgNodes.isEmpty()) {
258 errorString = QCoreApplication::translate(
"KMLHelper", _errorPrefix).arg(QCoreApplication::translate(
"KML",
"Unable to find LineString node in KML"));
262 for (
int nodeIdx = 0; nodeIdx < rgNodes.count(); nodeIdx++) {
263 const QDomNode lineStringNode = rgNodes.item(nodeIdx);
266 const QDomNode coordinatesNode = lineStringNode.namedItem(
"coordinates");
267 if (coordinatesNode.isNull()) {
268 qCWarning(KMLHelperLog) <<
"LineString" << nodeIdx << QStringLiteral(
"(line %1)").arg(lineStringNode.lineNumber())
269 <<
"missing coordinates node, skipping";
273 QList<QGeoCoordinate> rgCoords;
275 qCWarning(KMLHelperLog) <<
"LineString" << nodeIdx << QStringLiteral(
"(line %1)").arg(coordinatesNode.lineNumber())
281 if (rgCoords.count() < 2) {
282 qCWarning(KMLHelperLog) <<
"LineString" << nodeIdx << QStringLiteral(
"(line %1)").arg(lineStringNode.lineNumber())
283 <<
"has fewer than 2 vertices, skipping";
288 polylines.append(rgCoords);
291 if (polylines.isEmpty()) {
292 errorString = QCoreApplication::translate(
"KMLHelper", _errorPrefix).arg(QCoreApplication::translate(
"KML",
"No valid polylines found in KML file"));
309 const QDomNodeList rgNodes = domDocument.elementsByTagName(
"Point");
310 if (rgNodes.isEmpty()) {
311 errorString = QCoreApplication::translate(
"KMLHelper", _errorPrefix).arg(QCoreApplication::translate(
"KML",
"Unable to find Point node in KML"));
315 for (
int nodeIdx = 0; nodeIdx < rgNodes.count(); nodeIdx++) {
316 const QDomNode pointNode = rgNodes.item(nodeIdx);
319 const QDomNode coordinatesNode = pointNode.namedItem(
"coordinates");
320 if (coordinatesNode.isNull()) {
321 qCWarning(KMLHelperLog) <<
"Point" << nodeIdx << QStringLiteral(
"(line %1)").arg(pointNode.lineNumber())
322 <<
"missing coordinates node, skipping";
326 QList<QGeoCoordinate> coords;
328 qCWarning(KMLHelperLog) <<
"Point" << nodeIdx << QStringLiteral(
"(line %1)").arg(coordinatesNode.lineNumber())
334 if (!coords.isEmpty()) {
335 points.append(coords.first());
339 if (points.isEmpty()) {
340 errorString = QCoreApplication::translate(
"KMLHelper", _errorPrefix).arg(QCoreApplication::translate(
"KML",
"No valid points found in KML file"));