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