QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
OsmParser.cc
Go to the documentation of this file.
1#include "OsmParser.h"
2
3#include "Fact.h"
4#include "OsmParserThread.h"
6#include "SettingsManager.h"
7#include "Viewer3DSettings.h"
8
9#include <QtCore/QThread>
10
11#include <mapbox/earcut.hpp>
12
13#include <array>
14
15QGC_LOGGING_CATEGORY(OsmParserLog, "Viewer3d.OsmParser")
16
17OsmParser::OsmParser(QObject *parent)
18 : Viewer3DMapProvider{parent}
19 , _osmParserWorker(new OsmParserThread(this))
20{
22 _setBuildingLevelHeight(viewer3DSettings->buildingLevelHeight()->rawValue());
23 connect(viewer3DSettings->buildingLevelHeight(), &Fact::rawValueChanged, this, &OsmParser::_setBuildingLevelHeight);
24 connect(_osmParserWorker, &OsmParserThread::fileParsed, this, &OsmParser::_onOsmParserFinished);
25}
26
28{
29 // Stop the worker thread's event loop and wait for it to finish.
30 // Once the thread is joined, no events are being processed and
31 // the destructor chain is safe to run from our thread.
32 _osmParserWorker->thread()->quit();
33 _osmParserWorker->thread()->wait();
34 delete _osmParserWorker;
35}
36
37void OsmParser::setGpsRef(const QGeoCoordinate &gpsRef)
38{
39 _gpsRefPoint = gpsRef;
40 _gpsRefSet = true;
41 emit gpsRefChanged(_gpsRefPoint, _gpsRefSet);
42}
43
45{
46 _gpsRefPoint = QGeoCoordinate(0, 0, 0);
47 _gpsRefSet = false;
48 emit gpsRefChanged(_gpsRefPoint, _gpsRefSet);
49}
50
51void OsmParser::_setBuildingLevelHeight(const QVariant &value)
52{
53 _buildingLevelHeight = value.toFloat();
55}
56
57void OsmParser::_onOsmParserFinished(bool isValid)
58{
59 if (isValid) {
60 if (!_gpsRefSet) {
61 setGpsRef(_osmParserWorker->gpsRefPoint());
62
63 _coordinateMin = _osmParserWorker->coordinateMin();
64 _coordinateMax = _osmParserWorker->coordinateMax();
65 }
66 _mapLoadedFlag = true;
67 emit mapChanged();
68 qCDebug(OsmParserLog) << _osmParserWorker->mapBuildings().size() << "buildings loaded";
69 }
70}
71
72void OsmParser::parseOsmFile(const QString &filePath)
73{
74 _gpsRefSet = false;
75 if (_mapLoadedFlag) {
76 _mapLoadedFlag = false;
77 emit mapChanged();
78 }
80
81 _osmParserWorker->start(filePath);
82}
83
85{
86 QByteArray vertexData;
87 const auto &buildings = _osmParserWorker->mapBuildings();
88 vertexData.reserve(static_cast<qsizetype>(buildings.size()) * 1000);
89
90 for (auto it = buildings.begin(), end = buildings.end(); it != end; ++it) {
91 const auto &building = it.value();
92 float buildingHeight = 0;
93
94 std::vector<std::array<float, 2>> allPoints;
95 std::vector<std::array<float, 2>> ringPoints;
96 std::vector<std::vector<std::array<float, 2>>> polygon;
97 std::vector<QVector3D> mesh;
98
99 if (building.height > 0) {
100 buildingHeight = building.height;
101 } else if (building.levels > 0) {
102 buildingHeight = static_cast<float>(building.levels) * _buildingLevelHeight;
103 } else {
104 continue;
105 }
106
107 for (const auto &pt : building.points_local) {
108 ringPoints.push_back({pt.x(), pt.y()});
109 allPoints.push_back({pt.x(), pt.y()});
110 }
111 polygon.push_back(ringPoints);
112
113 ringPoints.clear();
114 for (const auto &pt : building.points_local_inner) {
115 ringPoints.push_back({pt.x(), pt.y()});
116 allPoints.push_back({pt.x(), pt.y()});
117 }
118 if (!ringPoints.empty()) {
119 polygon.push_back(ringPoints);
120 }
121
122 const std::vector<uint32_t> indices = mapbox::earcut<uint32_t>(polygon);
123
124 for (size_t i = 0; i < indices.size(); i += 3) {
125 uint32_t idx = indices[i];
126 mesh.push_back(QVector3D(allPoints[idx][0], allPoints[idx][1], buildingHeight));
127 idx = indices[i + 1];
128 mesh.push_back(QVector3D(allPoints[idx][0], allPoints[idx][1], buildingHeight));
129 idx = indices[i + 2];
130 mesh.push_back(QVector3D(allPoints[idx][0], allPoints[idx][1], buildingHeight));
131
132 idx = indices[i + 2];
133 mesh.push_back(QVector3D(allPoints[idx][0], allPoints[idx][1], 0));
134 idx = indices[i + 1];
135 mesh.push_back(QVector3D(allPoints[idx][0], allPoints[idx][1], 0));
136 idx = indices[i];
137 mesh.push_back(QVector3D(allPoints[idx][0], allPoints[idx][1], 0));
138 }
139
140 if (buildingHeight > 0) {
141 _triangulateWallsExtrudedPolygon(mesh, building.points_local, buildingHeight, false);
142 _triangulateWallsExtrudedPolygon(mesh, building.points_local, buildingHeight, true);
143
144 _triangulateWallsExtrudedPolygon(mesh, building.points_local_inner, buildingHeight, false);
145 _triangulateWallsExtrudedPolygon(mesh, building.points_local_inner, buildingHeight, true);
146 }
147
148 QByteArray buildingData(mesh.size() * 3 * sizeof(float), Qt::Initialization::Uninitialized);
149 float *p = reinterpret_cast<float *>(buildingData.data());
150
151 for (const auto &vertex : mesh) {
152 *p++ = vertex.x();
153 *p++ = vertex.y();
154 *p++ = vertex.z();
155 }
156
157 vertexData.append(buildingData);
158 }
159 return vertexData;
160}
161
162void OsmParser::_triangulateWallsExtrudedPolygon(std::vector<QVector3D> &triangulatedMesh, const std::vector<QVector2D> &verticesCcw, float h, bool inverseOrder)
163{
164 std::vector<QVector3D> quad(4);
165 const size_t verticesSize = verticesCcw.size();
166
167 if (inverseOrder) {
168 for (size_t i = 0; i < verticesSize; i++) {
169 const size_t next = (i + 1 < verticesSize) ? (i + 1) : size_t{0};
170 quad[0] = QVector3D(verticesCcw[next].x(), verticesCcw[next].y(), 0);
171 quad[1] = QVector3D(verticesCcw[i].x(), verticesCcw[i].y(), 0);
172 quad[2] = QVector3D(verticesCcw[i].x(), verticesCcw[i].y(), h);
173 quad[3] = QVector3D(verticesCcw[next].x(), verticesCcw[next].y(), h);
174 _triangulateRectangle(triangulatedMesh, quad, false);
175 }
176 _triangulateRectangle(triangulatedMesh, quad, true);
177 } else {
178 for (size_t i = 0; i < verticesSize; i++) {
179 const size_t next = (i + 1 < verticesSize) ? (i + 1) : size_t{0};
180 quad[0] = QVector3D(verticesCcw[i].x(), verticesCcw[i].y(), 0);
181 quad[1] = QVector3D(verticesCcw[next].x(), verticesCcw[next].y(), 0);
182 quad[2] = QVector3D(verticesCcw[next].x(), verticesCcw[next].y(), h);
183 quad[3] = QVector3D(verticesCcw[i].x(), verticesCcw[i].y(), h);
184 _triangulateRectangle(triangulatedMesh, quad, false);
185 }
186 _triangulateRectangle(triangulatedMesh, quad, true);
187 }
188}
189
190void OsmParser::_triangulateRectangle(std::vector<QVector3D> &triangulatedMesh, const std::vector<QVector3D> &verticesCcw, bool invertNormal)
191{
192 using Vec3i = std::array<unsigned int, 3>;
193 std::array<Vec3i, 2> meshIndices;
194
195 if (invertNormal) {
196 meshIndices[0] = {3, 1, 0};
197 meshIndices[1] = {3, 2, 1};
198 } else {
199 meshIndices[0] = {0, 1, 3};
200 meshIndices[1] = {1, 2, 3};
201 }
202
203 for (const auto &tri : meshIndices) {
204 for (unsigned int vi : tri) {
205 triangulatedMesh.push_back(verticesCcw[vi]);
206 }
207 }
208}
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void rawValueChanged(const QVariant &value)
const QGeoCoordinate & coordinateMin() const
void fileParsed(bool isValid)
const QGeoCoordinate & coordinateMax() const
void start(const QString &filePath)
const QMap< uint64_t, BuildingType_t > & mapBuildings() const
const QGeoCoordinate & gpsRefPoint() const
void parseOsmFile(const QString &filePath)
Definition OsmParser.cc:72
void buildingLevelHeightChanged()
QGeoCoordinate gpsRef() const override
Definition OsmParser.h:29
void resetGpsRef()
Definition OsmParser.cc:44
~OsmParser() override
Definition OsmParser.cc:27
QByteArray buildingToMesh()
Definition OsmParser.cc:84
void setGpsRef(const QGeoCoordinate &gpsRef)
Definition OsmParser.cc:37
Viewer3DSettings * viewer3DSettings() const
static SettingsManager * instance()
void gpsRefChanged(QGeoCoordinate newGpsRef, bool isRefSet)