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 _mapLoadedFlag = false;
77
78 _osmParserWorker->start(filePath);
79}
80
82{
83 QByteArray vertexData;
84 const auto &buildings = _osmParserWorker->mapBuildings();
85 vertexData.reserve(static_cast<qsizetype>(buildings.size()) * 1000);
86
87 for (auto it = buildings.begin(), end = buildings.end(); it != end; ++it) {
88 const auto &building = it.value();
89 float buildingHeight = 0;
90
91 std::vector<std::array<float, 2>> allPoints;
92 std::vector<std::array<float, 2>> ringPoints;
93 std::vector<std::vector<std::array<float, 2>>> polygon;
94 std::vector<QVector3D> mesh;
95
96 if (building.height > 0) {
97 buildingHeight = building.height;
98 } else if (building.levels > 0) {
99 buildingHeight = static_cast<float>(building.levels) * _buildingLevelHeight;
100 } else {
101 continue;
102 }
103
104 for (const auto &pt : building.points_local) {
105 ringPoints.push_back({pt.x(), pt.y()});
106 allPoints.push_back({pt.x(), pt.y()});
107 }
108 polygon.push_back(ringPoints);
109
110 ringPoints.clear();
111 for (const auto &pt : building.points_local_inner) {
112 ringPoints.push_back({pt.x(), pt.y()});
113 allPoints.push_back({pt.x(), pt.y()});
114 }
115 if (!ringPoints.empty()) {
116 polygon.push_back(ringPoints);
117 }
118
119 const std::vector<uint32_t> indices = mapbox::earcut<uint32_t>(polygon);
120
121 for (size_t i = 0; i < indices.size(); i += 3) {
122 uint32_t idx = indices[i];
123 mesh.push_back(QVector3D(allPoints[idx][0], allPoints[idx][1], buildingHeight));
124 idx = indices[i + 1];
125 mesh.push_back(QVector3D(allPoints[idx][0], allPoints[idx][1], buildingHeight));
126 idx = indices[i + 2];
127 mesh.push_back(QVector3D(allPoints[idx][0], allPoints[idx][1], buildingHeight));
128
129 idx = indices[i + 2];
130 mesh.push_back(QVector3D(allPoints[idx][0], allPoints[idx][1], 0));
131 idx = indices[i + 1];
132 mesh.push_back(QVector3D(allPoints[idx][0], allPoints[idx][1], 0));
133 idx = indices[i];
134 mesh.push_back(QVector3D(allPoints[idx][0], allPoints[idx][1], 0));
135 }
136
137 if (buildingHeight > 0) {
138 _triangulateWallsExtrudedPolygon(mesh, building.points_local, buildingHeight, false);
139 _triangulateWallsExtrudedPolygon(mesh, building.points_local, buildingHeight, true);
140
141 _triangulateWallsExtrudedPolygon(mesh, building.points_local_inner, buildingHeight, false);
142 _triangulateWallsExtrudedPolygon(mesh, building.points_local_inner, buildingHeight, true);
143 }
144
145 QByteArray buildingData(mesh.size() * 3 * sizeof(float), Qt::Initialization::Uninitialized);
146 float *p = reinterpret_cast<float *>(buildingData.data());
147
148 for (const auto &vertex : mesh) {
149 *p++ = vertex.x();
150 *p++ = vertex.y();
151 *p++ = vertex.z();
152 }
153
154 vertexData.append(buildingData);
155 }
156 return vertexData;
157}
158
159void OsmParser::_triangulateWallsExtrudedPolygon(std::vector<QVector3D> &triangulatedMesh, const std::vector<QVector2D> &verticesCcw, float h, bool inverseOrder)
160{
161 std::vector<QVector3D> quad(4);
162 const size_t verticesSize = verticesCcw.size();
163
164 if (inverseOrder) {
165 for (size_t i = 0; i < verticesSize; i++) {
166 const size_t next = (i + 1 < verticesSize) ? (i + 1) : size_t{0};
167 quad[0] = QVector3D(verticesCcw[next].x(), verticesCcw[next].y(), 0);
168 quad[1] = QVector3D(verticesCcw[i].x(), verticesCcw[i].y(), 0);
169 quad[2] = QVector3D(verticesCcw[i].x(), verticesCcw[i].y(), h);
170 quad[3] = QVector3D(verticesCcw[next].x(), verticesCcw[next].y(), h);
171 _triangulateRectangle(triangulatedMesh, quad, false);
172 }
173 _triangulateRectangle(triangulatedMesh, quad, true);
174 } else {
175 for (size_t i = 0; i < verticesSize; i++) {
176 const size_t next = (i + 1 < verticesSize) ? (i + 1) : size_t{0};
177 quad[0] = QVector3D(verticesCcw[i].x(), verticesCcw[i].y(), 0);
178 quad[1] = QVector3D(verticesCcw[next].x(), verticesCcw[next].y(), 0);
179 quad[2] = QVector3D(verticesCcw[next].x(), verticesCcw[next].y(), h);
180 quad[3] = QVector3D(verticesCcw[i].x(), verticesCcw[i].y(), h);
181 _triangulateRectangle(triangulatedMesh, quad, false);
182 }
183 _triangulateRectangle(triangulatedMesh, quad, true);
184 }
185}
186
187void OsmParser::_triangulateRectangle(std::vector<QVector3D> &triangulatedMesh, const std::vector<QVector3D> &verticesCcw, bool invertNormal)
188{
189 using Vec3i = std::array<unsigned int, 3>;
190 std::array<Vec3i, 2> meshIndices;
191
192 if (invertNormal) {
193 meshIndices[0] = {3, 1, 0};
194 meshIndices[1] = {3, 2, 1};
195 } else {
196 meshIndices[0] = {0, 1, 3};
197 meshIndices[1] = {1, 2, 3};
198 }
199
200 for (const auto &tri : meshIndices) {
201 for (unsigned int vi : tri) {
202 triangulatedMesh.push_back(verticesCcw[vi]);
203 }
204 }
205}
#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:81
void setGpsRef(const QGeoCoordinate &gpsRef)
Definition OsmParser.cc:37
Viewer3DSettings * viewer3DSettings() const
static SettingsManager * instance()
void gpsRefChanged(QGeoCoordinate newGpsRef, bool isRefSet)