7#include <QtCore/QFileInfo>
11#include <osmium/handler.hpp>
12#include <osmium/io/reader.hpp>
13#include <osmium/io/xml_input.hpp>
14#include <osmium/visitor.hpp>
29 const QStringList &singleStorey,
30 const QStringList &doubleStoreyLeisure)
32 , _singleStorey(singleStorey)
33 , _doubleStoreyLeisure(doubleStoreyLeisure)
36 void node(
const osmium::Node &node)
38 const int64_t nodeId = node.id();
44 coord.setLatitude(node.location().lat());
45 coord.setLongitude(node.location().lon());
48 nodes.insert(
static_cast<uint64_t
>(nodeId), coord);
51 void way(
const osmium::Way &way)
53 const int64_t wayId = way.id();
59 std::vector<QGeoCoordinate> gpsPoints;
60 std::vector<QVector2D> localPoints;
61 double lonMax = -1e10, lonMin = 1e10;
62 double latMax = -1e10, latMin = 1e10;
63 double xMax = -1e10, xMin = 1e10;
64 double yMax = -1e10, yMin = 1e10;
66 for (
const auto &nr : way.nodes()) {
67 const int64_t refId = nr.ref();
72 auto it = nodes.constFind(
static_cast<uint64_t
>(refId));
73 if (it == nodes.constEnd()) {
77 const QGeoCoordinate &gpsCoord = it.value();
78 gpsPoints.push_back(gpsCoord);
80 localPoints.push_back(QVector2D(localPt.x(), localPt.y()));
82 xMax = std::fmax(xMax, localPt.x());
83 yMax = std::fmax(yMax, localPt.y());
84 xMin = std::fmin(xMin, localPt.x());
85 yMin = std::fmin(yMin, localPt.y());
87 lonMax = std::fmax(lonMax, gpsCoord.longitude());
88 latMax = std::fmax(latMax, gpsCoord.latitude());
89 lonMin = std::fmin(lonMin, gpsCoord.longitude());
90 latMin = std::fmin(latMin, gpsCoord.latitude());
93 for (
const auto &tag : way.tags()) {
94 const QString key = QString::fromUtf8(tag.key());
95 if (key == QStringLiteral(
"building:levels")) {
96 building.
levels = QString::fromUtf8(tag.value()).toFloat();
97 }
else if (key == QStringLiteral(
"height")) {
98 building.
height = QString::fromUtf8(tag.value()).toFloat();
99 }
else if (key == QStringLiteral(
"building") && building.
levels == 0 && building.
height == 0) {
100 const QString value = QString::fromUtf8(tag.value());
101 if (_singleStorey.contains(value)) {
106 }
else if (key == QStringLiteral(
"leisure") && building.
levels == 0 && building.
height == 0) {
107 const QString value = QString::fromUtf8(tag.value());
108 if (_doubleStoreyLeisure.contains(value)) {
114 if (gpsPoints.size() > 2) {
116 coordMin.setLatitude(std::fmin(coordMin.latitude(), latMin));
117 coordMin.setLongitude(std::fmin(coordMin.longitude(), lonMin));
118 coordMax.setLatitude(std::fmax(coordMax.latitude(), latMax));
119 coordMax.setLongitude(std::fmax(coordMax.longitude(), lonMax));
123 building.
bb_max = QVector2D(xMax, yMax);
124 building.
bb_min = QVector2D(xMin, yMin);
125 buildings.insert(
static_cast<uint64_t
>(wayId), building);
131 const int64_t relationId = relation.id();
132 if (relationId == 0) {
137 std::vector<int64_t> idsToRemove;
138 bool isBuilding =
false;
139 bool isMultipolygon =
false;
141 for (
const auto &member : relation.members()) {
142 if (member.type() == osmium::item_type::way) {
143 const int64_t refId = member.ref();
144 const QString role = QString::fromUtf8(member.role());
145 auto bldItem = buildings.find(
static_cast<uint64_t
>(refId));
146 if (bldItem != buildings.end()) {
147 building.
append(bldItem.value().points_local, role == QStringLiteral(
"inner"));
148 building.
append(bldItem.value().points_gps, role == QStringLiteral(
"inner"));
149 building.
levels = std::fmax(building.
levels, bldItem.value().levels);
150 building.
height = std::fmax(building.
height, bldItem.value().height);
152 building.
bb_max[0] = std::fmax(building.
bb_max[0], bldItem.value().bb_max[0]);
153 building.
bb_max[1] = std::fmax(building.
bb_max[1], bldItem.value().bb_max[1]);
154 building.
bb_min[0] = std::fmin(building.
bb_min[0], bldItem.value().bb_min[0]);
155 building.
bb_min[1] = std::fmin(building.
bb_min[1], bldItem.value().bb_min[1]);
156 idsToRemove.push_back(refId);
161 for (
const auto &tag : relation.tags()) {
162 const QString key = QString::fromUtf8(tag.key());
163 if (key == QStringLiteral(
"type")) {
164 if (QString::fromUtf8(tag.value()) == QStringLiteral(
"multipolygon")) {
165 isMultipolygon =
true;
167 }
else if (key == QStringLiteral(
"building")) {
173 if (building.
height == 0) {
178 if (isMultipolygon && !idsToRemove.empty()) {
179 for (int64_t
id : idsToRemove) {
180 buildings.remove(
static_cast<uint64_t
>(
id));
182 buildings.insert(
static_cast<uint64_t
>(idsToRemove[0]), building);
186 QMap<uint64_t, QGeoCoordinate>
nodes;
187 QMap<uint64_t, OsmParserThread::BuildingType_t>
buildings;
192 const QGeoCoordinate &_gpsRef;
193 const QStringList &_singleStorey;
194 const QStringList &_doubleStoreyLeisure;
204 target.insert(target.end(), newPoints.begin(), newPoints.end());
209 auto &target = isInner ? points_local_inner : points_local;
210 target.insert(target.end(), newPoints.begin(), newPoints.end());
219 , _workerThread(new QThread())
223 this->moveToThread(_workerThread);
224 _workerThread->start();
229 _workerThread->quit();
230 _workerThread->wait();
231 delete _workerThread;
239void OsmParserThread::_parseOsmFile(
const QString &filePath)
242 _mapBuildings.clear();
244 if (filePath.isEmpty()) {
245 if (_mapLoadedFlag) {
246 qCDebug(OsmParserThreadLog,
"The 3D View has been cleared!");
248 qCDebug(OsmParserThreadLog,
"No OSM File is selected!");
250 _mapLoadedFlag =
false;
254 _mapLoadedFlag =
false;
256 QString resolvedPath = filePath;
258 if (!QDir::isAbsolutePath(resolvedPath)) {
259 resolvedPath = QStringLiteral(
"/") + filePath;
263 QFileInfo fileInfo(resolvedPath);
264 if (!fileInfo.exists() || !fileInfo.isFile()) {
265 qCDebug(OsmParserThreadLog) <<
"OSM file does not exist:" << resolvedPath;
269 const QString suffix = fileInfo.suffix().toLower();
270 if (suffix != QStringLiteral(
"osm") && suffix != QStringLiteral(
"xml")) {
271 qCDebug(OsmParserThreadLog) <<
"Invalid file extension:" << suffix;
277 osmium::io::File inputFile{resolvedPath.toStdString()};
278 osmium::io::Reader reader{inputFile, osmium::osm_entity_bits::all};
280 const auto &header = reader.header();
281 bool hasHeaderBounds = !header.boxes().empty();
282 if (hasHeaderBounds) {
283 const auto &box = header.boxes().front();
284 _coordinateMin = QGeoCoordinate(box.bottom_left().lat(), box.bottom_left().lon(), 0);
285 _coordinateMax = QGeoCoordinate(box.top_right().lat(), box.top_right().lon(), 0);
286 _gpsRefPoint = QGeoCoordinate(
287 0.5 * (_coordinateMin.latitude() + _coordinateMax.latitude()),
288 0.5 * (_coordinateMin.longitude() + _coordinateMax.longitude()), 0);
291 OsmBuildingHandler handler(_gpsRefPoint, _singleStoreyBuildings, _doubleStoreyLeisure);
292 handler.coordMin = _coordinateMin;
293 handler.coordMax = _coordinateMax;
294 osmium::apply(reader, handler);
297 _mapNodes = std::move(handler.nodes);
298 _mapBuildings = std::move(handler.buildings);
299 if (!hasHeaderBounds) {
301 if (_mapNodes.isEmpty()) {
306 double minLat = std::numeric_limits<double>::max();
307 double minLon = std::numeric_limits<double>::max();
308 double maxLat = std::numeric_limits<double>::lowest();
309 double maxLon = std::numeric_limits<double>::lowest();
311 for (
auto it = _mapNodes.cbegin(); it != _mapNodes.cend(); ++it) {
312 minLat = std::fmin(minLat, it.value().latitude());
313 minLon = std::fmin(minLon, it.value().longitude());
314 maxLat = std::fmax(maxLat, it.value().latitude());
315 maxLon = std::fmax(maxLon, it.value().longitude());
318 _coordinateMin = QGeoCoordinate(minLat, minLon, 0);
319 _coordinateMax = QGeoCoordinate(maxLat, maxLon, 0);
320 _gpsRefPoint = QGeoCoordinate(0.5 * (minLat + maxLat), 0.5 * (minLon + maxLon), 0);
324 for (
auto it = _mapBuildings.begin(); it != _mapBuildings.end(); ++it) {
325 BuildingType_t &building = it.value();
326 building.points_local.clear();
327 building.points_local_inner.clear();
329 building.bb_max = QVector2D(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest());
330 building.bb_min = QVector2D(std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
332 for (
const QGeoCoordinate &gpsCoord : building.points_gps) {
334 const QVector2D local2D(localPt.x(), localPt.y());
335 building.points_local.push_back(local2D);
337 building.bb_max[0] = std::fmax(building.bb_max[0], local2D.x());
338 building.bb_max[1] = std::fmax(building.bb_max[1], local2D.y());
339 building.bb_min[0] = std::fmin(building.bb_min[0], local2D.x());
340 building.bb_min[1] = std::fmin(building.bb_min[1], local2D.y());
343 for (
const QGeoCoordinate &gpsCoord : building.points_gps_inner) {
345 const QVector2D local2D(localPt.x(), localPt.y());
346 building.points_local_inner.push_back(local2D);
348 building.bb_max[0] = std::fmax(building.bb_max[0], local2D.x());
349 building.bb_max[1] = std::fmax(building.bb_max[1], local2D.y());
350 building.bb_min[0] = std::fmin(building.bb_min[0], local2D.x());
351 building.bb_min[1] = std::fmin(building.bb_min[1], local2D.y());
355 _coordinateMin = handler.coordMin;
356 _coordinateMax = handler.coordMax;
359 _mapLoadedFlag =
true;
361 }
catch (
const std::exception &e) {
362 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