7#include <QtCore/QFileInfo>
8#include <QtCore/QThread>
10#include <osmium/handler.hpp>
11#include <osmium/io/reader.hpp>
12#include <osmium/io/xml_input.hpp>
13#include <osmium/visitor.hpp>
30 const QStringList &singleStorey,
31 const QStringList &doubleStoreyLeisure)
33 , _singleStorey(singleStorey)
34 , _doubleStoreyLeisure(doubleStoreyLeisure)
37 void node(
const osmium::Node &node)
39 const int64_t nodeId = node.id();
45 coord.setLatitude(node.location().lat());
46 coord.setLongitude(node.location().lon());
49 nodes.insert(
static_cast<uint64_t
>(nodeId), coord);
52 void way(
const osmium::Way &way)
54 const int64_t wayId = way.id();
60 std::vector<QGeoCoordinate> gpsPoints;
61 std::vector<QVector2D> localPoints;
62 double lonMax = -1e10, lonMin = 1e10;
63 double latMax = -1e10, latMin = 1e10;
64 double xMax = -1e10, xMin = 1e10;
65 double yMax = -1e10, yMin = 1e10;
67 for (
const auto &nr : way.nodes()) {
68 const int64_t refId = nr.ref();
73 auto it = nodes.constFind(
static_cast<uint64_t
>(refId));
74 if (it == nodes.constEnd()) {
78 const QGeoCoordinate &gpsCoord = it.value();
79 gpsPoints.push_back(gpsCoord);
81 localPoints.push_back(QVector2D(localPt.x(), localPt.y()));
83 xMax = std::fmax(xMax, localPt.x());
84 yMax = std::fmax(yMax, localPt.y());
85 xMin = std::fmin(xMin, localPt.x());
86 yMin = std::fmin(yMin, localPt.y());
88 lonMax = std::fmax(lonMax, gpsCoord.longitude());
89 latMax = std::fmax(latMax, gpsCoord.latitude());
90 lonMin = std::fmin(lonMin, gpsCoord.longitude());
91 latMin = std::fmin(latMin, gpsCoord.latitude());
94 for (
const auto &tag : way.tags()) {
95 const QString key = QString::fromUtf8(tag.key());
96 if (key == QStringLiteral(
"building:levels")) {
97 building.
levels = QString::fromUtf8(tag.value()).toFloat();
98 }
else if (key == QStringLiteral(
"height")) {
99 building.
height = QString::fromUtf8(tag.value()).toFloat();
100 }
else if (key == QStringLiteral(
"building") && building.
levels == 0 && building.
height == 0) {
101 const QString value = QString::fromUtf8(tag.value());
102 if (_singleStorey.contains(value)) {
107 }
else if (key == QStringLiteral(
"leisure") && building.
levels == 0 && building.
height == 0) {
108 const QString value = QString::fromUtf8(tag.value());
109 if (_doubleStoreyLeisure.contains(value)) {
115 if (gpsPoints.size() > 2) {
117 coordMin.setLatitude(std::fmin(coordMin.latitude(), latMin));
118 coordMin.setLongitude(std::fmin(coordMin.longitude(), lonMin));
119 coordMax.setLatitude(std::fmax(coordMax.latitude(), latMax));
120 coordMax.setLongitude(std::fmax(coordMax.longitude(), lonMax));
124 building.
bb_max = QVector2D(xMax, yMax);
125 building.
bb_min = QVector2D(xMin, yMin);
126 buildings.insert(
static_cast<uint64_t
>(wayId), building);
132 const int64_t relationId = relation.id();
133 if (relationId == 0) {
138 std::vector<int64_t> idsToRemove;
139 bool isBuilding =
false;
140 bool isMultipolygon =
false;
142 for (
const auto &member : relation.members()) {
143 if (member.type() == osmium::item_type::way) {
144 const int64_t refId = member.ref();
145 const QString role = QString::fromUtf8(member.role());
146 auto bldItem = buildings.find(
static_cast<uint64_t
>(refId));
147 if (bldItem != buildings.end()) {
148 building.
append(bldItem.value().points_local, role == QStringLiteral(
"inner"));
149 building.
append(bldItem.value().points_gps, role == QStringLiteral(
"inner"));
150 building.
levels = std::fmax(building.
levels, bldItem.value().levels);
151 building.
height = std::fmax(building.
height, bldItem.value().height);
153 building.
bb_max[0] = std::fmax(building.
bb_max[0], bldItem.value().bb_max[0]);
154 building.
bb_max[1] = std::fmax(building.
bb_max[1], bldItem.value().bb_max[1]);
155 building.
bb_min[0] = std::fmin(building.
bb_min[0], bldItem.value().bb_min[0]);
156 building.
bb_min[1] = std::fmin(building.
bb_min[1], bldItem.value().bb_min[1]);
157 idsToRemove.push_back(refId);
162 for (
const auto &tag : relation.tags()) {
163 const QString key = QString::fromUtf8(tag.key());
164 if (key == QStringLiteral(
"type")) {
165 if (QString::fromUtf8(tag.value()) == QStringLiteral(
"multipolygon")) {
166 isMultipolygon =
true;
168 }
else if (key == QStringLiteral(
"building")) {
174 if (building.
height == 0) {
179 if (isMultipolygon && !idsToRemove.empty()) {
180 for (int64_t
id : idsToRemove) {
181 buildings.remove(
static_cast<uint64_t
>(
id));
183 buildings.insert(
static_cast<uint64_t
>(idsToRemove[0]), building);
187 QMap<uint64_t, QGeoCoordinate>
nodes;
188 QMap<uint64_t, OsmParserThread::BuildingType_t>
buildings;
193 const QGeoCoordinate &_gpsRef;
194 const QStringList &_singleStorey;
195 const QStringList &_doubleStoreyLeisure;
205 target.insert(target.end(), newPoints.begin(), newPoints.end());
210 auto &target = isInner ? points_local_inner : points_local;
211 target.insert(target.end(), newPoints.begin(), newPoints.end());
220 , _workerThread(new QThread())
224 this->moveToThread(_workerThread);
225 _workerThread->start();
230 _workerThread->quit();
231 _workerThread->wait();
232 delete _workerThread;
240void OsmParserThread::_parseOsmFile(
const QString &filePath)
243 _mapBuildings.clear();
245 if (filePath.isEmpty()) {
246 if (_mapLoadedFlag) {
247 qCDebug(OsmParserThreadLog,
"The 3D View has been cleared!");
249 qCDebug(OsmParserThreadLog,
"No OSM File is selected!");
251 _mapLoadedFlag =
false;
255 _mapLoadedFlag =
false;
257 QString resolvedPath = filePath;
259 if (!QDir::isAbsolutePath(resolvedPath)) {
260 resolvedPath = QStringLiteral(
"/") + filePath;
264 QFileInfo fileInfo(resolvedPath);
265 if (!fileInfo.exists() || !fileInfo.isFile()) {
266 qCDebug(OsmParserThreadLog) <<
"OSM file does not exist:" << resolvedPath;
270 const QString suffix = fileInfo.suffix().toLower();
271 if (suffix != QStringLiteral(
"osm") && suffix != QStringLiteral(
"xml")) {
272 qCDebug(OsmParserThreadLog) <<
"Invalid file extension:" << suffix;
278 osmium::io::File inputFile{resolvedPath.toStdString()};
279 osmium::io::Reader reader{inputFile, osmium::osm_entity_bits::all};
281 const auto &header = reader.header();
282 bool hasHeaderBounds = !header.boxes().empty();
283 if (hasHeaderBounds) {
284 const auto &box = header.boxes().front();
285 _coordinateMin = QGeoCoordinate(box.bottom_left().lat(), box.bottom_left().lon(), 0);
286 _coordinateMax = QGeoCoordinate(box.top_right().lat(), box.top_right().lon(), 0);
287 _gpsRefPoint = QGeoCoordinate(
288 0.5 * (_coordinateMin.latitude() + _coordinateMax.latitude()),
289 0.5 * (_coordinateMin.longitude() + _coordinateMax.longitude()), 0);
292 OsmBuildingHandler handler(_gpsRefPoint, _singleStoreyBuildings, _doubleStoreyLeisure);
293 handler.coordMin = _coordinateMin;
294 handler.coordMax = _coordinateMax;
295 osmium::apply(reader, handler);
298 _mapNodes = std::move(handler.nodes);
299 _mapBuildings = std::move(handler.buildings);
300 if (!hasHeaderBounds) {
302 if (_mapNodes.isEmpty()) {
307 double minLat = std::numeric_limits<double>::max();
308 double minLon = std::numeric_limits<double>::max();
309 double maxLat = std::numeric_limits<double>::lowest();
310 double maxLon = std::numeric_limits<double>::lowest();
312 for (
auto it = _mapNodes.cbegin(); it != _mapNodes.cend(); ++it) {
313 minLat = std::fmin(minLat, it.value().latitude());
314 minLon = std::fmin(minLon, it.value().longitude());
315 maxLat = std::fmax(maxLat, it.value().latitude());
316 maxLon = std::fmax(maxLon, it.value().longitude());
319 _coordinateMin = QGeoCoordinate(minLat, minLon, 0);
320 _coordinateMax = QGeoCoordinate(maxLat, maxLon, 0);
321 _gpsRefPoint = QGeoCoordinate(0.5 * (minLat + maxLat), 0.5 * (minLon + maxLon), 0);
325 for (
auto it = _mapBuildings.begin(); it != _mapBuildings.end(); ++it) {
326 BuildingType_t &building = it.value();
327 building.points_local.clear();
328 building.points_local_inner.clear();
330 building.bb_max = QVector2D(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest());
331 building.bb_min = QVector2D(std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
333 for (
const QGeoCoordinate &gpsCoord : building.points_gps) {
335 const QVector2D local2D(localPt.x(), localPt.y());
336 building.points_local.push_back(local2D);
338 building.bb_max[0] = std::fmax(building.bb_max[0], local2D.x());
339 building.bb_max[1] = std::fmax(building.bb_max[1], local2D.y());
340 building.bb_min[0] = std::fmin(building.bb_min[0], local2D.x());
341 building.bb_min[1] = std::fmin(building.bb_min[1], local2D.y());
344 for (
const QGeoCoordinate &gpsCoord : building.points_gps_inner) {
346 const QVector2D local2D(localPt.x(), localPt.y());
347 building.points_local_inner.push_back(local2D);
349 building.bb_max[0] = std::fmax(building.bb_max[0], local2D.x());
350 building.bb_max[1] = std::fmax(building.bb_max[1], local2D.y());
351 building.bb_min[0] = std::fmin(building.bb_min[0], local2D.x());
352 building.bb_min[1] = std::fmin(building.bb_min[1], local2D.y());
356 _coordinateMin = handler.coordMin;
357 _coordinateMax = handler.coordMax;
360 _mapLoadedFlag =
true;
362 }
catch (
const std::exception &e) {
363 qCDebug(OsmParserThreadLog) <<
"OSM parse error:" << e.what();
Geographic coordinate conversion utilities using GeographicLib.
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void way(const osmium::Way &way)
OsmBuildingHandler(const QGeoCoordinate &gpsRef, const QStringList &singleStorey, const QStringList &doubleStoreyLeisure)
QMap< uint64_t, OsmParserThread::BuildingType_t > buildings
QMap< uint64_t, QGeoCoordinate > nodes
void node(const osmium::Node &node)
void relation(const osmium::Relation &relation)
OsmParserThread(QObject *parent=nullptr)
void fileParsed(bool isValid)
void start(const QString &filePath)
void startThread(const QString &filePath)
QVector3D convertGpsToEnu(const QGeoCoordinate &coord, const QGeoCoordinate &ref)
void append(const std::vector< QGeoCoordinate > &newPoints, bool isInner)
std::vector< QGeoCoordinate > points_gps
std::vector< QVector2D > points_local
std::vector< QGeoCoordinate > points_gps_inner