diff --git a/src/lib/marble/geodata/CMakeLists.txt b/src/lib/marble/geodata/CMakeLists.txt --- a/src/lib/marble/geodata/CMakeLists.txt +++ b/src/lib/marble/geodata/CMakeLists.txt @@ -234,7 +234,7 @@ geodata/graphicsitem/GeoPhotoGraphicsItem.cpp geodata/graphicsitem/GeoPolygonGraphicsItem.cpp geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.cpp - geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.cpp + geodata/graphicsitem/BuildingGraphicsItem.cpp geodata/graphicsitem/GeoTrackGraphicsItem.cpp geodata/graphicsitem/ScreenOverlayGraphicsItem.cpp ) diff --git a/src/lib/marble/geodata/data/GeoDataBuilding.h b/src/lib/marble/geodata/data/GeoDataBuilding.h --- a/src/lib/marble/geodata/data/GeoDataBuilding.h +++ b/src/lib/marble/geodata/data/GeoDataBuilding.h @@ -14,6 +14,7 @@ #include #include "GeoDataGeometry.h" +#include "GeoDataCoordinates.h" #include "geodata_export.h" @@ -36,6 +37,11 @@ explicit GeoDataBuilding(const GeoDataGeometry &other); explicit GeoDataBuilding(const GeoDataBuilding &other); + struct NamedEntry { + GeoDataCoordinates point; + QString label; + }; + GeoDataBuilding& operator=(const GeoDataBuilding &other); const char *nodeType() const override; @@ -103,10 +109,33 @@ /*! - * @return the multigeometry associated with the building + @return the multigeometry associated with the building */ GeoDataMultiGeometry* multiGeometry() const; + +/*! + @return the latlonaltbox for the contained multigeometry + */ + const GeoDataLatLonAltBox& latLonAltBox() const override; + + +/*! + @return the name of the building + */ + QString name() const; + + +/*! + Sets the name of the building + @param name + */ + void setName(const QString& name); + + QVector entries() const; + + void setEntries(const QVector& entries); + private: GeoDataBuildingPrivate* const d; }; diff --git a/src/lib/marble/geodata/data/GeoDataBuilding.cpp b/src/lib/marble/geodata/data/GeoDataBuilding.cpp --- a/src/lib/marble/geodata/data/GeoDataBuilding.cpp +++ b/src/lib/marble/geodata/data/GeoDataBuilding.cpp @@ -104,4 +104,29 @@ return &d->m_multiGeometry; } +const GeoDataLatLonAltBox &GeoDataBuilding::latLonAltBox() const +{ + return d->m_multiGeometry.latLonAltBox(); +} + +QString GeoDataBuilding::name() const +{ + return d->m_name; +} + +void GeoDataBuilding::setName(const QString& name) +{ + d->m_name = name; +} + +QVector GeoDataBuilding::entries() const +{ + return d->m_entries; +} + +void GeoDataBuilding::setEntries(const QVector &entries) +{ + d->m_entries = entries; +} + } diff --git a/src/lib/marble/geodata/data/GeoDataBuilding_p.h b/src/lib/marble/geodata/data/GeoDataBuilding_p.h --- a/src/lib/marble/geodata/data/GeoDataBuilding_p.h +++ b/src/lib/marble/geodata/data/GeoDataBuilding_p.h @@ -22,7 +22,8 @@ GeoDataBuildingPrivate() : m_height(0.0), m_minLevel(0), - m_maxLevel(0) + m_maxLevel(0), + m_name(QString()) { } @@ -38,6 +39,8 @@ int m_maxLevel; QVector m_nonExistentLevels; GeoDataMultiGeometry m_multiGeometry; + QString m_name; + QVector m_entries; }; } diff --git a/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.h b/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.h --- a/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.h +++ b/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.h @@ -23,12 +23,14 @@ class GeoDataLinearRing; class GeoDataPlacemark; class GeoDataPolygon; +class GeoDataBuilding; class MARBLE_EXPORT AbstractGeoPolygonGraphicsItem : public GeoGraphicsItem { protected: explicit AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon); explicit AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring); + explicit AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building); ~AbstractGeoPolygonGraphicsItem() override; public: @@ -36,6 +38,9 @@ void paint(GeoPainter* painter, const ViewportParams *viewport, const QString &layer, int tileZoomLevel) override; bool contains(const QPoint &screenPosition, const ViewportParams *viewport) const override; + void setLinearRing(GeoDataLinearRing* ring); + void setPolygon(GeoDataPolygon* polygon); + static const void *s_previousStyle; protected: @@ -44,14 +49,17 @@ const GeoDataPolygon *polygon() const { return m_polygon; } inline const GeoDataLinearRing *ring() const { return m_ring; } + inline + const GeoDataBuilding *building() const { return m_building; } static int extractElevation(const GeoDataPlacemark &placemark); private: QPixmap texture(const QString &path, const QColor &color); - const GeoDataPolygon *const m_polygon; - const GeoDataLinearRing *const m_ring; + const GeoDataPolygon * m_polygon; + const GeoDataLinearRing * m_ring; + const GeoDataBuilding *const m_building; }; } diff --git a/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.cpp b/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.cpp --- a/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.cpp +++ b/src/lib/marble/geodata/graphicsitem/AbstractGeoPolygonGraphicsItem.cpp @@ -12,6 +12,7 @@ #include "GeoDataLinearRing.h" #include "GeoDataPolygon.h" +#include "GeoDataBuilding.h" #include "GeoPainter.h" #include "GeoDataLatLonAltBox.h" #include "GeoDataStyle.h" @@ -36,14 +37,24 @@ AbstractGeoPolygonGraphicsItem::AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon) : GeoGraphicsItem(placemark), m_polygon(polygon), - m_ring(0) + m_ring(0), + m_building(0) { } AbstractGeoPolygonGraphicsItem::AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring) : GeoGraphicsItem(placemark), m_polygon(0), - m_ring(ring) + m_ring(ring), + m_building(0) +{ +} + +AbstractGeoPolygonGraphicsItem::AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building) : + GeoGraphicsItem(placemark), + m_polygon(0), + m_ring(0), + m_building(building) { } @@ -53,11 +64,13 @@ const GeoDataLatLonAltBox& AbstractGeoPolygonGraphicsItem::latLonAltBox() const { - if( m_polygon ) { + if(m_polygon) { return m_polygon->latLonAltBox(); + } else if (m_ring) { + return m_ring->latLonAltBox(); } - return m_ring->latLonAltBox(); + return m_building->latLonAltBox(); } void AbstractGeoPolygonGraphicsItem::paint( GeoPainter* painter, const ViewportParams* viewport, const QString &layer, int tileZoomLevel) @@ -210,4 +223,14 @@ return texture; } +void AbstractGeoPolygonGraphicsItem::setLinearRing(GeoDataLinearRing *ring) +{ + m_ring = ring; +} + +void AbstractGeoPolygonGraphicsItem::setPolygon(GeoDataPolygon *polygon) +{ + m_polygon = polygon; +} + } diff --git a/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.h b/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.h --- a/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.h +++ b/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.h @@ -19,22 +19,16 @@ namespace Marble { -class MARBLE_EXPORT BuildingGeoPolygonGraphicsItem : public AbstractGeoPolygonGraphicsItem +class MARBLE_EXPORT BuildingGraphicsItem : public AbstractGeoPolygonGraphicsItem { public: - explicit BuildingGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon); - explicit BuildingGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring); - ~BuildingGeoPolygonGraphicsItem() override; + explicit BuildingGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building); + ~BuildingGraphicsItem() override; public: void paint(GeoPainter* painter, const ViewportParams *viewport, const QString &layer, int tileZoomLevel) override; private: - struct NamedEntry { - GeoDataCoordinates point; - QString label; - }; - void paintFrame(GeoPainter* painter, const ViewportParams *viewport); void paintRoof(GeoPainter* painter, const ViewportParams *viewport); @@ -56,14 +50,7 @@ bool contains(const QPoint &screenPosition, const ViewportParams *viewport) const override; - static double extractBuildingHeight(const GeoDataPlacemark &placemark); - static QString extractBuildingLabel(const GeoDataPlacemark &placemark); - static QVector extractNamedEntries(const GeoDataPlacemark &placemark); - private: - const double m_buildingHeight; - const QString m_buildingText; - const QVector m_entries; QVector m_cachedOuterPolygons; QVector m_cachedInnerPolygons; QVector m_cachedOuterRoofPolygons; diff --git a/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.cpp b/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.cpp --- a/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.cpp +++ b/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.cpp @@ -8,7 +8,7 @@ // Copyright 2011 Konstantin Oblaukhov // -#include "BuildingGeoPolygonGraphicsItem.h" +#include "BuildingGraphicsItem.h" #include "MarbleDebug.h" #include "ViewportParams.h" @@ -16,6 +16,8 @@ #include "GeoDataPlacemark.h" #include "GeoDataLinearRing.h" #include "GeoDataPolygon.h" +#include "GeoDataBuilding.h" +#include "GeoDataMultiGeometry.h" #include "GeoDataPolyStyle.h" #include "OsmPlacemarkData.h" #include "GeoPainter.h" @@ -26,32 +28,17 @@ namespace Marble { -BuildingGeoPolygonGraphicsItem::BuildingGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, - const GeoDataPolygon *polygon) - : AbstractGeoPolygonGraphicsItem(placemark, polygon) - , m_buildingHeight(extractBuildingHeight(*placemark)) - , m_buildingText(extractBuildingLabel(*placemark)) - , m_entries(extractNamedEntries(*placemark)) - , m_hasInnerBoundaries(false) +BuildingGraphicsItem::BuildingGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building) + : AbstractGeoPolygonGraphicsItem(placemark, building) { - setZValue(m_buildingHeight); - Q_ASSERT(m_buildingHeight > 0.0); - - QStringList paintLayers; - paintLayers << QStringLiteral("Polygon/Building/frame") - << QStringLiteral("Polygon/Building/roof"); - setPaintLayers(paintLayers); -} + if (const auto ring = geodata_cast(&building->multiGeometry()->at(0))) { + setLinearRing(ring); + } else if (const auto poly = geodata_cast(&building->multiGeometry()->at(0))) { + setPolygon(poly); + } -BuildingGeoPolygonGraphicsItem::BuildingGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, - const GeoDataLinearRing* ring) - : AbstractGeoPolygonGraphicsItem(placemark, ring) - , m_buildingHeight(extractBuildingHeight(*placemark)) - , m_buildingText(extractBuildingLabel(*placemark)) - , m_entries(extractNamedEntries(*placemark)) -{ - setZValue(m_buildingHeight); - Q_ASSERT(m_buildingHeight > 0.0); + setZValue(building->height()); + Q_ASSERT(building->height() > 0.0); QStringList paintLayers; paintLayers << QStringLiteral("Polygon/Building/frame") @@ -59,7 +46,7 @@ setPaintLayers(paintLayers); } -BuildingGeoPolygonGraphicsItem::~BuildingGeoPolygonGraphicsItem() +BuildingGraphicsItem::~BuildingGraphicsItem() { qDeleteAll(m_cachedOuterPolygons); qDeleteAll(m_cachedInnerPolygons); @@ -67,8 +54,8 @@ qDeleteAll(m_cachedInnerRoofPolygons); } -void BuildingGeoPolygonGraphicsItem::initializeBuildingPainting(const GeoPainter* painter, const ViewportParams *viewport, - bool &drawAccurate3D, bool &isCameraAboveBuilding ) const +void BuildingGraphicsItem::initializeBuildingPainting(const GeoPainter* painter, const ViewportParams *viewport, + bool &drawAccurate3D, bool &isCameraAboveBuilding ) const { drawAccurate3D = false; isCameraAboveBuilding = false; @@ -82,10 +69,10 @@ drawAccurate3D = painter->mapQuality() == HighQuality ? maxOffset > pixelSize : maxOffset > 1.5 * pixelSize; } -void BuildingGeoPolygonGraphicsItem::updatePolygons( const ViewportParams *viewport, - QVector& outerPolygons, - QVector& innerPolygons, - bool &hasInnerBoundaries ) +void BuildingGraphicsItem::updatePolygons(const ViewportParams *viewport, + QVector& outerPolygons, + QVector& innerPolygons, + bool &hasInnerBoundaries ) { // Since subtracting one fully contained polygon from another results in a single // polygon with a "connecting line" between the inner and outer part we need @@ -103,7 +90,7 @@ } } -QPointF BuildingGeoPolygonGraphicsItem::centroid(const QPolygonF &polygon, double &area) +QPointF BuildingGraphicsItem::centroid(const QPolygonF &polygon, double &area) { auto centroid = QPointF(0.0, 0.0); area = 0.0; @@ -123,11 +110,11 @@ return area != 0 ? centroid / (6.0*area) : polygon.boundingRect().center(); } -QPointF BuildingGeoPolygonGraphicsItem::buildingOffset(const QPointF &point, const ViewportParams *viewport, bool* isCameraAboveBuilding) const +QPointF BuildingGraphicsItem::buildingOffset(const QPointF &point, const ViewportParams *viewport, bool* isCameraAboveBuilding) const { qreal const cameraFactor = 0.5 * tan(0.5 * 110 * DEG2RAD); - Q_ASSERT(m_buildingHeight > 0.0); - qreal const buildingFactor = m_buildingHeight / EARTH_RADIUS; + Q_ASSERT(building()->height() > 0.0); + qreal const buildingFactor = building()->height() / EARTH_RADIUS; qreal const cameraHeightPixel = viewport->width() * cameraFactor; qreal buildingHeightPixel = viewport->radius() * buildingFactor; @@ -155,102 +142,7 @@ return QPointF(shiftX, shiftY); } -double BuildingGeoPolygonGraphicsItem::extractBuildingHeight(const GeoDataPlacemark &placemark) -{ - double height = 8.0; - - const OsmPlacemarkData &osmData = placemark.osmData(); - - QHash::const_iterator tagIter; - if ((tagIter = osmData.findTag(QStringLiteral("height"))) != osmData.tagsEnd()) { - if (tagIter.value().contains(QChar('m')) || - tagIter.value().contains(QStringLiteral("meter")) || - tagIter.value().contains(QStringLiteral("meters")) || - tagIter.value().contains(QStringLiteral("metre")) || - tagIter.value().contains(QStringLiteral("metres"))) { - QString const heightValue = QString(tagIter.value()).remove(QChar('m')) - .remove(QStringLiteral("meters")).remove(QStringLiteral("meter")) - .remove(QStringLiteral("metre")).remove(QStringLiteral("metres")).trimmed(); - bool extracted = false; - double extractedHeight = heightValue.toDouble(&extracted); - if (extracted) { - height = extractedHeight; - } - } else { - double extractedHeight = 0.0; // in inches - if (tagIter.value().contains(QChar('\''))) { - double heightInches = 0.0; - QStringList const feetInches = tagIter.value().split(QChar('\'')); - bool okFeet; - double feet = feetInches[0].trimmed().toDouble(&okFeet); - if (okFeet) { - heightInches = feet * 12.0; - } - if (!feetInches[1].isEmpty()) { // has inches as unit as well - bool okInches; - double inches = QString(feetInches[1]).remove(QChar('\"')).trimmed().toDouble(&okInches); - if (okInches) { - heightInches += inches; - } - } - extractedHeight = heightInches; - } else if (tagIter.value().contains(QStringLiteral("feet"))) { - bool ok; - double feet = QString(tagIter.value()).remove(QStringLiteral("feet")).trimmed().toDouble(&ok); - if (ok) { - extractedHeight = feet * 12.0; - } - } - if (extractedHeight > 0.0) { - height = extractedHeight * 0.0254; // convert inches to meters - } - } - } else if ((tagIter = osmData.findTag(QStringLiteral("building:levels"))) != osmData.tagsEnd()) { - int const levels = tagIter.value().toInt(); - int const skipLevels = osmData.tagValue(QStringLiteral("building:min_level")).toInt(); - /** @todo Is 35 as an upper bound for the number of levels sane? */ - height = 3.0 * qBound(1, 1+levels-skipLevels, 35); - } - - return qBound(1.0, height, 1000.0); -} - -QString BuildingGeoPolygonGraphicsItem::extractBuildingLabel(const GeoDataPlacemark &placemark) -{ - const OsmPlacemarkData &osmData = placemark.osmData(); - - auto tagIter = osmData.findTag(QStringLiteral("addr:housename")); - if (tagIter != osmData.tagsEnd()) { - return tagIter.value(); - } - - tagIter = osmData.findTag(QStringLiteral("addr:housenumber")); - if (tagIter != osmData.tagsEnd()) { - return tagIter.value(); - } - - return QString(); -} - -QVector BuildingGeoPolygonGraphicsItem::extractNamedEntries(const GeoDataPlacemark &placemark) -{ - QVector entries; - - const auto end = placemark.osmData().nodeReferencesEnd(); - for (auto iter = placemark.osmData().nodeReferencesBegin(); iter != end; ++iter) { - const auto tagIter = iter.value().findTag(QStringLiteral("addr:housenumber")); - if (tagIter != iter.value().tagsEnd()) { - NamedEntry entry; - entry.point = iter.key(); - entry.label = tagIter.value(); - entries.push_back(entry); - } - } - - return entries; -} - -void BuildingGeoPolygonGraphicsItem::paint(GeoPainter* painter, const ViewportParams* viewport, const QString &layer, int tileZoomLevel) +void BuildingGraphicsItem::paint(GeoPainter* painter, const ViewportParams* viewport, const QString &layer, int tileZoomLevel) { // Just display flat buildings for tile level 17 if (tileZoomLevel == 17) { @@ -260,7 +152,7 @@ } return; } - setZValue(m_buildingHeight); + setZValue(building()->height()); // For level 18, 19 .. render 3D buildings in perspective if (layer.endsWith(QLatin1String("/frame"))) { @@ -289,7 +181,7 @@ } } -void BuildingGeoPolygonGraphicsItem::paintRoof(GeoPainter* painter, const ViewportParams* viewport) +void BuildingGraphicsItem::paintRoof(GeoPainter* painter, const ViewportParams* viewport) { bool drawAccurate3D; bool isCameraAboveBuilding; @@ -389,7 +281,7 @@ QPointF roofCenter; // Label position calculation - if (!m_buildingText.isEmpty() || !m_entries.isEmpty()) { + if (!building()->name().isEmpty() || !building()->entries().isEmpty()) { QSizeF const polygonSize = outerRoof->boundingRect().size(); qreal size = polygonSize.width() * polygonSize.height(); if (size > maxSize) { @@ -401,8 +293,8 @@ } // Draw the housenumber labels - if (drawAccurate3D && !m_buildingText.isEmpty() && !roofCenter.isNull()) { - double const w2 = 0.5 * painter->fontMetrics().width(m_buildingText); + if (drawAccurate3D && !building()->name().isEmpty() && !roofCenter.isNull()) { + double const w2 = 0.5 * painter->fontMetrics().width(building()->name()); double const ascent = painter->fontMetrics().ascent(); double const descent = painter->fontMetrics().descent(); double const a2 = 0.5 * painter->fontMetrics().ascent(); @@ -412,30 +304,30 @@ && outerRoof->containsPoint(textPosition + QPointF(2+2*w2, descent), Qt::OddEvenFill) && outerRoof->containsPoint(textPosition + QPointF(2+2*w2, -ascent), Qt::OddEvenFill) ) { - painter->drawTextFragment(roofCenter.toPoint(), m_buildingText, + painter->drawTextFragment(roofCenter.toPoint(), building()->name(), painter->font().pointSize(), painter->brush().color()); } } } // Render additional housenumbers at building entries - if (!m_entries.isEmpty() && maxArea > 1600 * m_entries.size()) { - for(const auto &entry: m_entries) { + if (!building()->entries().isEmpty() && maxArea > 1600 * building()->entries().size()) { + for(const auto &entry: building()->entries()) { qreal x, y; viewport->screenCoordinates(entry.point, x, y); QPointF point(x, y); point += buildingOffset(point, viewport); painter->drawTextFragment(point.toPoint(), - m_buildingText, painter->font().pointSize(), painter->brush().color(), + building()->name(), painter->font().pointSize(), painter->brush().color(), GeoPainter::RoundFrame); } } } -void BuildingGeoPolygonGraphicsItem::paintFrame(GeoPainter *painter, const ViewportParams *viewport) +void BuildingGraphicsItem::paintFrame(GeoPainter *painter, const ViewportParams *viewport) { // TODO: how does this match the Q_ASSERT in the constructor? - if (m_buildingHeight == 0.0) { + if (building()->height() == 0.0) { return; } @@ -527,7 +419,7 @@ } } -void BuildingGeoPolygonGraphicsItem::screenPolygons(const ViewportParams *viewport, const GeoDataPolygon *polygon, +void BuildingGraphicsItem::screenPolygons(const ViewportParams *viewport, const GeoDataPolygon *polygon, QVector &innerPolygons, QVector &outerPolygons ) @@ -548,7 +440,7 @@ } } -bool BuildingGeoPolygonGraphicsItem::contains(const QPoint &screenPosition, const ViewportParams *viewport) const +bool BuildingGraphicsItem::contains(const QPoint &screenPosition, const ViewportParams *viewport) const { if (m_cachedOuterPolygons.isEmpty()) { // Level 17 @@ -579,7 +471,7 @@ return false; } -bool BuildingGeoPolygonGraphicsItem::configurePainterForFrame(GeoPainter *painter) const +bool BuildingGraphicsItem::configurePainterForFrame(GeoPainter *painter) const { QPen currentPen = painter->pen(); diff --git a/src/lib/marble/geodata/graphicsitem/GeoPolygonGraphicsItem.h b/src/lib/marble/geodata/graphicsitem/GeoPolygonGraphicsItem.h --- a/src/lib/marble/geodata/graphicsitem/GeoPolygonGraphicsItem.h +++ b/src/lib/marble/geodata/graphicsitem/GeoPolygonGraphicsItem.h @@ -18,12 +18,14 @@ class GeoDataLinearRing; class GeoDataPolygon; +class GeoDataBuilding; class MARBLE_EXPORT GeoPolygonGraphicsItem : public AbstractGeoPolygonGraphicsItem { public: static AbstractGeoPolygonGraphicsItem *createGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon); static AbstractGeoPolygonGraphicsItem *createGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring); + static AbstractGeoPolygonGraphicsItem *createGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building); explicit GeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon); explicit GeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring); diff --git a/src/lib/marble/geodata/graphicsitem/GeoPolygonGraphicsItem.cpp b/src/lib/marble/geodata/graphicsitem/GeoPolygonGraphicsItem.cpp --- a/src/lib/marble/geodata/graphicsitem/GeoPolygonGraphicsItem.cpp +++ b/src/lib/marble/geodata/graphicsitem/GeoPolygonGraphicsItem.cpp @@ -10,7 +10,7 @@ #include "GeoPolygonGraphicsItem.h" -#include "BuildingGeoPolygonGraphicsItem.h" +#include "BuildingGraphicsItem.h" #include "GeoDataPlacemark.h" #include "StyleBuilder.h" @@ -19,20 +19,18 @@ AbstractGeoPolygonGraphicsItem *GeoPolygonGraphicsItem::createGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon) { - if (placemark->visualCategory() == GeoDataPlacemark::Building) { - return new BuildingGeoPolygonGraphicsItem(placemark, polygon); - } return new GeoPolygonGraphicsItem(placemark, polygon); } AbstractGeoPolygonGraphicsItem *GeoPolygonGraphicsItem::createGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring) { - if (placemark->visualCategory() == GeoDataPlacemark::Building) { - return new BuildingGeoPolygonGraphicsItem(placemark, ring); - } return new GeoPolygonGraphicsItem(placemark, ring); } +AbstractGeoPolygonGraphicsItem *GeoPolygonGraphicsItem::createGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building) +{ + return new BuildingGraphicsItem(placemark, building); +} GeoPolygonGraphicsItem::GeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon) : AbstractGeoPolygonGraphicsItem(placemark, polygon) diff --git a/src/lib/marble/layers/GeometryLayer.cpp b/src/lib/marble/layers/GeometryLayer.cpp --- a/src/lib/marble/layers/GeometryLayer.cpp +++ b/src/lib/marble/layers/GeometryLayer.cpp @@ -24,6 +24,7 @@ #include "GeoDataLinearRing.h" #include "GeoDataMultiGeometry.h" #include "GeoDataPolygon.h" +#include "GeoDataBuilding.h" #include "GeoDataPolyStyle.h" #include "GeoDataStyle.h" #include "GeoDataIconStyle.h" @@ -412,6 +413,8 @@ if (item->zValue() == 0) { item->setZValue(poly->renderOrder()); } + } else if (const auto building = geodata_cast(object)) { + item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, building); } else if (const auto multigeo = geodata_cast(object)) { int rowCount = multigeo->size(); for (int row = 0; row < rowCount; ++row) { diff --git a/src/plugins/runner/osm/OsmWay.h b/src/plugins/runner/osm/OsmWay.h --- a/src/plugins/runner/osm/OsmWay.h +++ b/src/plugins/runner/osm/OsmWay.h @@ -12,6 +12,7 @@ #define MARBLE_OSMWAY #include "OsmNode.h" +#include "GeoDataBuilding.h" #include #include @@ -38,10 +39,19 @@ static bool isAreaTag(const StyleBuilder::OsmTag &keyValue); + bool isBuilding() const; + + static bool isBuildingTag(const StyleBuilder::OsmTag &keyValue); + OsmPlacemarkData m_osmData; QVector m_references; static QSet s_areaTags; + static QSet s_buildingTags; + + QString extractBuildingName() const; + double extractBuildingHeight() const; + QVector extractNamedEntries() const; }; typedef QHash OsmWays; diff --git a/src/plugins/runner/osm/OsmWay.cpp b/src/plugins/runner/osm/OsmWay.cpp --- a/src/plugins/runner/osm/OsmWay.cpp +++ b/src/plugins/runner/osm/OsmWay.cpp @@ -18,10 +18,12 @@ #include #include #include +#include namespace Marble { QSet OsmWay::s_areaTags; +QSet OsmWay::s_buildingTags; GeoDataPlacemark *OsmWay::create(const OsmNodes &nodes, QSet &usedNodes) const { @@ -45,7 +47,17 @@ usedNodes << nodeId; } - geometry = new GeoDataLinearRing(linearRing.optimized()); + if (isBuilding()) { + GeoDataBuilding building; + building.setName(extractBuildingName()); + building.setHeight(extractBuildingHeight()); + building.setEntries(extractNamedEntries()); + building.multiGeometry()->append(new GeoDataLinearRing(linearRing.optimized())); + + geometry = new GeoDataBuilding(building); + } else { + geometry = new GeoDataLinearRing(linearRing.optimized()); + } } else { GeoDataLineString lineString; lineString.reserve(m_references.size()); @@ -189,4 +201,118 @@ return s_areaTags.contains(keyValue); } +bool OsmWay::isBuilding() const +{ + for (auto iter = m_osmData.tagsBegin(), end=m_osmData.tagsEnd(); iter != end; ++iter) { + const auto tag = StyleBuilder::OsmTag(iter.key(), iter.value()); + if (isBuildingTag(tag)) { + return true; + } + } + + return false; +} + +bool OsmWay::isBuildingTag(const StyleBuilder::OsmTag &keyValue) +{ + if (s_buildingTags.isEmpty()) { + for (auto const & tag: StyleBuilder::buildingTags()) { + s_buildingTags.insert(tag); + } + } + + return s_buildingTags.contains(keyValue); +} + +QString OsmWay::extractBuildingName() const +{ + auto tagIter = m_osmData.findTag(QStringLiteral("addr:housename")); + if (tagIter != m_osmData.tagsEnd()) { + return tagIter.value(); + } + + tagIter = m_osmData.findTag(QStringLiteral("addr:housenumber")); + if (tagIter != m_osmData.tagsEnd()) { + return tagIter.value(); + } + + return QString(); +} + +double OsmWay::extractBuildingHeight() const +{ + double height = 8.0; + + QHash::const_iterator tagIter; + if ((tagIter = m_osmData.findTag(QStringLiteral("height"))) != m_osmData.tagsEnd()) { + if (tagIter.value().contains(QChar('m')) || + tagIter.value().contains(QStringLiteral("meter")) || + tagIter.value().contains(QStringLiteral("meters")) || + tagIter.value().contains(QStringLiteral("metre")) || + tagIter.value().contains(QStringLiteral("metres"))) { + QString const heightValue = QString(tagIter.value()).remove(QChar('m')) + .remove(QStringLiteral("meters")).remove(QStringLiteral("meter")) + .remove(QStringLiteral("metre")).remove(QStringLiteral("metres")).trimmed(); + bool extracted = false; + double extractedHeight = heightValue.toDouble(&extracted); + if (extracted) { + height = extractedHeight; + } + } else { + double extractedHeight = 0.0; // in inches + if (tagIter.value().contains(QChar('\''))) { + double heightInches = 0.0; + QStringList const feetInches = tagIter.value().split(QChar('\'')); + bool okFeet; + double feet = feetInches[0].trimmed().toDouble(&okFeet); + if (okFeet) { + heightInches = feet * 12.0; + } + if (!feetInches[1].isEmpty()) { // has inches as unit as well + bool okInches; + double inches = QString(feetInches[1]).remove(QChar('\"')).trimmed().toDouble(&okInches); + if (okInches) { + heightInches += inches; + } + } + extractedHeight = heightInches; + } else if (tagIter.value().contains(QStringLiteral("feet"))) { + bool ok; + double feet = QString(tagIter.value()).remove(QStringLiteral("feet")).trimmed().toDouble(&ok); + if (ok) { + extractedHeight = feet * 12.0; + } + } + if (extractedHeight > 0.0) { + height = extractedHeight * 0.0254; // convert inches to meters + } + } + } else if ((tagIter = m_osmData.findTag(QStringLiteral("building:levels"))) != m_osmData.tagsEnd()) { + int const levels = tagIter.value().toInt(); + int const skipLevels = m_osmData.tagValue(QStringLiteral("building:min_level")).toInt(); + /** @todo Is 35 as an upper bound for the number of levels sane? */ + height = 3.0 * qBound(1, 1+levels-skipLevels, 35); + } + + return qBound(1.0, height, 1000.0); +} + +QVector OsmWay::extractNamedEntries() const +{ + QVector entries; + + const auto end = m_osmData.nodeReferencesEnd(); + for (auto iter = m_osmData.nodeReferencesBegin(); iter != end; ++iter) { + const auto tagIter = iter.value().findTag(QStringLiteral("addr:housenumber")); + if (tagIter != iter.value().tagsEnd()) { + GeoDataBuilding::NamedEntry entry; + entry.point = iter.key(); + entry.label = tagIter.value(); + entries.push_back(entry); + } + } + + return entries; +} + } diff --git a/tools/vectorosm-tilecreator/NodeReducer.h b/tools/vectorosm-tilecreator/NodeReducer.h --- a/tools/vectorosm-tilecreator/NodeReducer.h +++ b/tools/vectorosm-tilecreator/NodeReducer.h @@ -30,6 +30,13 @@ bool touchesTileBorder(const GeoDataCoordinates &coordinates) const; void setBorderPoints(OsmPlacemarkData &osmData, const QVector &borderPoints, int length) const; + GeoDataLinearRing* reducedRing(const GeoDataLinearRing& prevRing, + GeoDataPlacemark* placemark, + const GeoDataPlacemark::GeoDataVisualCategory& visualCategory); + GeoDataPolygon* reducedPolygon(const GeoDataPolygon& prevPolygon, + GeoDataPlacemark* placemark, + const GeoDataPlacemark::GeoDataVisualCategory& visualCategory); + template void reduce(T const & lineString, OsmPlacemarkData& osmData, GeoDataPlacemark::GeoDataVisualCategory visualCategory, T* reducedLine) { diff --git a/tools/vectorosm-tilecreator/NodeReducer.cpp b/tools/vectorosm-tilecreator/NodeReducer.cpp --- a/tools/vectorosm-tilecreator/NodeReducer.cpp +++ b/tools/vectorosm-tilecreator/NodeReducer.cpp @@ -11,6 +11,8 @@ #include "GeoDataPlacemark.h" #include "GeoDataLineString.h" #include "GeoDataPolygon.h" +#include "GeoDataBuilding.h" +#include "GeoDataMultiGeometry.h" #include "GeoDataCoordinates.h" #include "MarbleMath.h" #include "NodeReducer.h" @@ -40,23 +42,21 @@ placemark->setGeometry(reducedLine); } else if (m_zoomLevel < 17) { if (const auto prevRing = geodata_cast(geometry)) { - GeoDataLinearRing* reducedRing = new GeoDataLinearRing; - reduce(*prevRing, placemark->osmData(), visualCategory, reducedRing); - placemark->setGeometry(reducedRing); + placemark->setGeometry(reducedRing(*prevRing, placemark, visualCategory)); } else if (const auto prevPolygon = geodata_cast(geometry)) { - GeoDataPolygon* reducedPolygon = new GeoDataPolygon; - GeoDataLinearRing const * prevRing = &(prevPolygon->outerBoundary()); - GeoDataLinearRing reducedRing; - reduce(*prevRing, placemark->osmData().memberReference(-1), visualCategory, &reducedRing); - reducedPolygon->setOuterBoundary(reducedRing); - QVector const & innerBoundaries = prevPolygon->innerBoundaries(); - for(int i = 0; i < innerBoundaries.size(); i++) { - prevRing = &innerBoundaries[i]; - GeoDataLinearRing reducedInnerRing; - reduce(*prevRing, placemark->osmData().memberReference(i), visualCategory, &reducedInnerRing); - reducedPolygon->appendInnerBoundary(reducedInnerRing); + placemark->setGeometry(reducedPolygon(*prevPolygon, placemark, visualCategory)); + } else if (const auto building = geodata_cast(geometry)) { + if (const auto prevRing = geodata_cast(&building->multiGeometry()->at(0))) { + GeoDataLinearRing* ring = reducedRing(*prevRing, placemark, visualCategory); + building->multiGeometry()->clear(); + building->multiGeometry()->append(ring); + placemark->setGeometry(new GeoDataBuilding(*building)); + } else if (const auto prevPolygon = geodata_cast(&building->multiGeometry()->at(0))) { + GeoDataPolygon* poly = reducedPolygon(*prevPolygon, placemark, visualCategory); + building->multiGeometry()->clear(); + building->multiGeometry()->append(poly); + placemark->setGeometry(new GeoDataBuilding(*building)); } - placemark->setGeometry(reducedPolygon); } } } @@ -187,4 +187,32 @@ osmData.addTag(QStringLiteral("mx:bp"), value.mid(1)); } +GeoDataLinearRing *NodeReducer::reducedRing(const GeoDataLinearRing& prevRing, + GeoDataPlacemark* placemark, + const GeoDataPlacemark::GeoDataVisualCategory& visualCategory) +{ + GeoDataLinearRing* reducedRing = new GeoDataLinearRing; + reduce(prevRing, placemark->osmData(), visualCategory, reducedRing); + return reducedRing; +} + +GeoDataPolygon *NodeReducer::reducedPolygon(const GeoDataPolygon& prevPolygon, + GeoDataPlacemark* placemark, + const GeoDataPlacemark::GeoDataVisualCategory& visualCategory) +{ + GeoDataPolygon* reducedPolygon = new GeoDataPolygon; + GeoDataLinearRing const * prevRing = &(prevPolygon.outerBoundary()); + GeoDataLinearRing reducedRing; + reduce(*prevRing, placemark->osmData().memberReference(-1), visualCategory, &reducedRing); + reducedPolygon->setOuterBoundary(reducedRing); + QVector const & innerBoundaries = prevPolygon.innerBoundaries(); + for(int i = 0; i < innerBoundaries.size(); i++) { + prevRing = &innerBoundaries[i]; + GeoDataLinearRing reducedInnerRing; + reduce(*prevRing, placemark->osmData().memberReference(i), visualCategory, &reducedInnerRing); + reducedPolygon->appendInnerBoundary(reducedInnerRing); + } + return reducedPolygon; +} + }