diff --git a/src/lib/marble/MarbleGlobal.h b/src/lib/marble/MarbleGlobal.h --- a/src/lib/marble/MarbleGlobal.h +++ b/src/lib/marble/MarbleGlobal.h @@ -210,6 +210,9 @@ const qreal M2IN = 39.3701; const qreal IN2M = 1.0 / M2IN; +// Interconversion between Imperial System: feet vs inch +const qreal FT2IN = 12.0; + // Conversion Metric / Imperial System: meter vs yard const qreal M2YD = 1.09361; const qreal YD2M = 1.0 / M2YD; 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,14 +37,21 @@ 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; EnumGeometryId geometryId() const override; GeoDataGeometry *copy() const override; + static double parseBuildingHeight(const QString& buildingHeight); + /*! Destroys the GeoDataBuilding */ @@ -103,10 +111,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,88 @@ return &d->m_multiGeometry; } +const GeoDataLatLonAltBox &GeoDataBuilding::latLonAltBox() const +{ + // @TODO: This is temporary, for only when we have just one child + Q_ASSERT(d->m_multiGeometry.size() == 1); + return d->m_multiGeometry.at(0).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; +} + +double GeoDataBuilding::parseBuildingHeight(const QString& buildingHeight) +{ + double height = 8.0; + + // check first for unitless value + bool converted; + double extractedHeight = buildingHeight.toDouble(&converted); + if (converted) { + return extractedHeight; + } + + if (buildingHeight.endsWith(QChar('m')) || + buildingHeight.endsWith(QLatin1String("meter")) || + buildingHeight.endsWith(QLatin1String("meters")) || + buildingHeight.endsWith(QLatin1String("metre")) || + buildingHeight.endsWith(QLatin1String("metres"))) { + QString const heightValue = QString(buildingHeight).remove(QStringLiteral("meters")) + .remove(QStringLiteral("meter")).remove(QStringLiteral("metres")) + .remove(QStringLiteral("metre")).remove(QChar('m')).trimmed(); + bool extracted; + double extractedHeight = heightValue.toDouble(&extracted); + if (extracted) { + height = extractedHeight; + } + } else { // feet and inches + double extractedHeight = 0.0; // in inches, converted to meters in the end + if (buildingHeight.contains(QChar('\''))) { + double heightInches = 0.0; + QStringList const feetInches = buildingHeight.split(QChar('\'')); + bool okFeet; + double feet = feetInches[0].trimmed().toDouble(&okFeet); + if (okFeet) { + heightInches = feet * FT2IN; + } + 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 (buildingHeight.endsWith(QLatin1String("feet"))) { + bool ok; + double feet = QString(buildingHeight).remove(QStringLiteral("feet")).trimmed().toDouble(&ok); + if (ok) { + extractedHeight = feet * FT2IN; + } + } + if (extractedHeight > 0.0) { + height = extractedHeight * IN2M; // convert inches to meters + } + } + + return height; +} + } 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 @@ -38,6 +38,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,35 +23,43 @@ 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); + AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon); + AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring); + AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building); ~AbstractGeoPolygonGraphicsItem() override; public: const GeoDataLatLonAltBox& latLonAltBox() const override; 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: bool configurePainter(GeoPainter* painter, const ViewportParams *viewport); inline 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,18 @@ return texture; } +void AbstractGeoPolygonGraphicsItem::setLinearRing(GeoDataLinearRing *ring) +{ + Q_ASSERT(m_building); + Q_ASSERT(!m_polygon); + m_ring = ring; +} + +void AbstractGeoPolygonGraphicsItem::setPolygon(GeoDataPolygon *polygon) +{ + Q_ASSERT(m_building); + Q_ASSERT(!m_ring); + m_polygon = polygon; +} + } diff --git a/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.h b/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.h rename from src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.h rename to src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.h --- a/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.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; + 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/BuildingGeoPolygonGraphicsItem.cpp b/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.cpp rename from src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.cpp rename to src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.cpp --- a/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.cpp +++ b/src/lib/marble/geodata/graphicsitem/BuildingGraphicsItem.cpp @@ -8,14 +8,16 @@ // Copyright 2011 Konstantin Oblaukhov // -#include "BuildingGeoPolygonGraphicsItem.h" +#include "BuildingGraphicsItem.h" #include "MarbleDebug.h" #include "ViewportParams.h" #include "GeoDataTypes.h" #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,49 +28,34 @@ 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") << QStringLiteral("Polygon/Building/roof"); setPaintLayers(paintLayers); } -BuildingGeoPolygonGraphicsItem::~BuildingGeoPolygonGraphicsItem() +BuildingGraphicsItem::~BuildingGraphicsItem() { qDeleteAll(m_cachedOuterPolygons); qDeleteAll(m_cachedInnerPolygons); qDeleteAll(m_cachedOuterRoofPolygons); 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,67 +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()) { - /** @todo Also parse non-SI units, see https://wiki.openstreetmap.org/wiki/Key:height#Height_of_buildings */ - QString const heightValue = QString(tagIter.value()).remove(QStringLiteral(" meters")).remove(QStringLiteral(" m")); - bool extracted = false; - double extractedHeight = heightValue.toDouble(&extracted); - if (extracted) { - height = extractedHeight; - } - } 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) { @@ -225,7 +152,7 @@ } return; } - setZValue(m_buildingHeight); + setZValue(building()->height()); // For level 18, 19 .. render 3D buildings in perspective if (layer.endsWith(QLatin1String("/frame"))) { @@ -254,7 +181,7 @@ } } -void BuildingGeoPolygonGraphicsItem::paintRoof(GeoPainter* painter, const ViewportParams* viewport) +void BuildingGraphicsItem::paintRoof(GeoPainter* painter, const ViewportParams* viewport) { bool drawAccurate3D; bool isCameraAboveBuilding; @@ -354,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) { @@ -366,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(); @@ -377,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; } @@ -492,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 ) @@ -513,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 @@ -544,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,29 +10,27 @@ #include "GeoPolygonGraphicsItem.h" -#include "BuildingGeoPolygonGraphicsItem.h" +#include "BuildingGraphicsItem.h" #include "GeoDataPlacemark.h" #include "StyleBuilder.h" namespace Marble { 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/lib/marble/osm/OsmObjectManager.cpp b/src/lib/marble/osm/OsmObjectManager.cpp --- a/src/lib/marble/osm/OsmObjectManager.cpp +++ b/src/lib/marble/osm/OsmObjectManager.cpp @@ -15,6 +15,8 @@ #include "GeoDataPlacemark.h" #include "GeoDataLinearRing.h" #include "GeoDataPolygon.h" +#include "GeoDataBuilding.h" +#include "GeoDataMultiGeometry.h" #include "osm/OsmPlacemarkData.h" namespace Marble { @@ -44,18 +46,32 @@ } } + const auto building = geodata_cast(placemark->geometry()); + + GeoDataLinearRing* lineString; + if (building) { + lineString = geodata_cast(&building->multiGeometry()->at(0)); + } else { + lineString = geodata_cast(placemark->geometry()); + } // Assigning osmData to each of the line's nodes ( if they don't already have data ) - if (const auto lineString = geodata_cast(placemark->geometry())) { + if (lineString) { for (auto it =lineString->constBegin(), end = lineString->constEnd(); it != end; ++it ) { if (osmData.nodeReference(*it).isNull()) { osmData.nodeReference(*it).setId(--m_minId); } } } + GeoDataPolygon* polygon; + if (building) { + polygon = geodata_cast(&building->multiGeometry()->at(0)); + } else { + polygon = geodata_cast(placemark->geometry()); + } // Assigning osmData to each of the polygons boundaries, and to each of the // nodes that are part of those boundaries ( if they don't already have data ) - if (const auto polygon = geodata_cast(placemark->geometry())) { + if (polygon) { const GeoDataLinearRing &outerBoundary = polygon->outerBoundary(); int index = -1; if ( isNull ) { 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,77 @@ 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()) { + height = GeoDataBuilding::parseBuildingHeight(tagIter.value()); + } 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/src/plugins/runner/osm/translators/O5mWriter.cpp b/src/plugins/runner/osm/translators/O5mWriter.cpp --- a/src/plugins/runner/osm/translators/O5mWriter.cpp +++ b/src/plugins/runner/osm/translators/O5mWriter.cpp @@ -16,6 +16,8 @@ #include "GeoDataPlacemark.h" #include "GeoDataRelation.h" #include "GeoDataPolygon.h" +#include "GeoDataBuilding.h" +#include "GeoDataMultiGeometry.h" #include "GeoWriter.h" #include "osm/OsmPlacemarkData.h" @@ -171,9 +173,15 @@ referencesBuffer.open(QIODevice::WriteOnly); QDataStream referencesStream(&referencesBuffer); if (const auto placemark = geodata_cast(relation.first)) { - auto polygon = geodata_cast(placemark->geometry()); - Q_ASSERT(polygon); - writeMultipolygonMembers(*polygon, lastReferenceId, osmData, stringTable, referencesStream); + if (const auto building = geodata_cast(placemark->geometry())) { + auto polygon = geodata_cast(&building->multiGeometry()->at(0)); + Q_ASSERT(polygon); + writeMultipolygonMembers(*polygon, lastReferenceId, osmData, stringTable, referencesStream); + } else { + auto polygon = geodata_cast(placemark->geometry()); + Q_ASSERT(polygon); + writeMultipolygonMembers(*polygon, lastReferenceId, osmData, stringTable, referencesStream); + } } else if (const auto placemark = geodata_cast(relation.first)) { writeRelationMembers(placemark, lastReferenceId, osmData, stringTable, referencesStream); } else { diff --git a/src/plugins/runner/osm/translators/OsmConverter.h b/src/plugins/runner/osm/translators/OsmConverter.h --- a/src/plugins/runner/osm/translators/OsmConverter.h +++ b/src/plugins/runner/osm/translators/OsmConverter.h @@ -18,7 +18,9 @@ class GeoDataLineString; class GeoDataDocument; +class GeoDataLinearRing; class GeoDataPolygon; +class GeoDataPlacemark; class GeoDataFeature; class OsmPlacemarkData; @@ -45,6 +47,12 @@ Nodes m_nodes; Ways m_ways; Relations m_relations; + + void processLinearRing(GeoDataLinearRing *linearRing, + const OsmPlacemarkData& osmData); + void processPolygon(GeoDataPolygon *polygon, + const OsmPlacemarkData& osmData, + GeoDataPlacemark* placemark); }; } diff --git a/src/plugins/runner/osm/translators/OsmConverter.cpp b/src/plugins/runner/osm/translators/OsmConverter.cpp --- a/src/plugins/runner/osm/translators/OsmConverter.cpp +++ b/src/plugins/runner/osm/translators/OsmConverter.cpp @@ -23,6 +23,8 @@ #include "GeoDataPolygon.h" #include "GeoDataRelation.h" #include "GeoDataLinearRing.h" +#include "GeoDataBuilding.h" +#include "GeoDataMultiGeometry.h" #include "osm/OsmPlacemarkData.h" #include "osm/OsmObjectManager.h" #include "OsmRelationTagWriter.h" @@ -53,31 +55,15 @@ } m_ways << OsmConverter::Way(lineString, osmData); } else if (const auto linearRing = geodata_cast(placemark->geometry())) { - for (auto const &coordinates: *linearRing) { - m_nodes << OsmConverter::Node(coordinates, osmData.nodeReference(coordinates)); - } - m_ways << OsmConverter::Way(linearRing, osmData); + processLinearRing(linearRing, osmData); } else if (const auto polygon = geodata_cast(placemark->geometry())) { - int index = -1; - - // Writing all the outerRing's nodes - const GeoDataLinearRing &outerRing = polygon->outerBoundary(); - const OsmPlacemarkData outerRingOsmData = osmData.memberReference( index ); - for (auto const &coordinates: outerRing) { - m_nodes << OsmConverter::Node(coordinates, outerRingOsmData.nodeReference(coordinates)); - } - m_ways << OsmConverter::Way(&outerRing, outerRingOsmData); - - // Writing all nodes for each innerRing - for (auto const &innerRing: polygon->innerBoundaries() ) { - ++index; - const OsmPlacemarkData innerRingOsmData = osmData.memberReference( index ); - for (auto const &coordinates: innerRing) { - m_nodes << OsmConverter::Node(coordinates, innerRingOsmData.nodeReference(coordinates)); - } - m_ways << OsmConverter::Way(&innerRing, innerRingOsmData); + processPolygon(polygon, osmData, placemark); + } else if (const auto building = geodata_cast(placemark->geometry())) { + if (const auto linearRing = geodata_cast(&building->multiGeometry()->at(0))) { + processLinearRing(linearRing, osmData); + } else if (const auto polygon = geodata_cast(&building->multiGeometry()->at(0))) { + processPolygon(polygon, osmData, placemark); } - m_relations.append(OsmConverter::Relation(placemark, osmData)); } } else if (const auto placemark = geodata_cast(feature)) { m_relations.append(OsmConverter::Relation(placemark, placemark->osmData())); @@ -105,5 +91,40 @@ return m_relations; } +void OsmConverter::processLinearRing(GeoDataLinearRing *linearRing, + const OsmPlacemarkData& osmData) +{ + for (auto const &coordinates: *linearRing) { + m_nodes << OsmConverter::Node(coordinates, osmData.nodeReference(coordinates)); + } + m_ways << OsmConverter::Way(linearRing, osmData); +} + +void OsmConverter::processPolygon(GeoDataPolygon *polygon, + const OsmPlacemarkData& osmData, + GeoDataPlacemark* placemark) +{ + int index = -1; + + // Writing all the outerRing's nodes + const GeoDataLinearRing &outerRing = polygon->outerBoundary(); + const OsmPlacemarkData outerRingOsmData = osmData.memberReference( index ); + for (auto const &coordinates: outerRing) { + m_nodes << OsmConverter::Node(coordinates, outerRingOsmData.nodeReference(coordinates)); + } + m_ways << OsmConverter::Way(&outerRing, outerRingOsmData); + + // Writing all nodes for each innerRing + for (auto const &innerRing: polygon->innerBoundaries() ) { + ++index; + const OsmPlacemarkData innerRingOsmData = osmData.memberReference( index ); + for (auto const &coordinates: innerRing) { + m_nodes << OsmConverter::Node(coordinates, innerRingOsmData.nodeReference(coordinates)); + } + m_ways << OsmConverter::Way(&innerRing, innerRingOsmData); + } + m_relations.append(OsmConverter::Relation(placemark, osmData)); +} + } diff --git a/src/plugins/runner/osm/translators/OsmDocumentTagTranslator.cpp b/src/plugins/runner/osm/translators/OsmDocumentTagTranslator.cpp --- a/src/plugins/runner/osm/translators/OsmDocumentTagTranslator.cpp +++ b/src/plugins/runner/osm/translators/OsmDocumentTagTranslator.cpp @@ -20,6 +20,8 @@ #include "GeoDataGeometry.h" #include "GeoDataPoint.h" #include "GeoDataPolygon.h" +#include "GeoDataBuilding.h" +#include "GeoDataMultiGeometry.h" #include "GeoDataPlacemark.h" #include "GeoDataLinearRing.h" #include "GeoDataTypes.h" @@ -56,9 +58,15 @@ for (auto const & relation: converter.relations()) { if (auto placemark = geodata_cast(relation.first)) { - auto polygon = geodata_cast(placemark->geometry()); - Q_ASSERT(polygon); - OsmRelationTagWriter::writeMultipolygon(*polygon, relation.second, writer ); + if (const auto building = geodata_cast(placemark->geometry())) { + auto polygon = geodata_cast(&building->multiGeometry()->at(0)); + Q_ASSERT(polygon); + OsmRelationTagWriter::writeMultipolygon(*polygon, relation.second, writer ); + } else { + auto polygon = geodata_cast(placemark->geometry()); + Q_ASSERT(polygon); + OsmRelationTagWriter::writeMultipolygon(*polygon, relation.second, writer ); + } } } diff --git a/tests/TestGeoDataBuilding.cpp b/tests/TestGeoDataBuilding.cpp --- a/tests/TestGeoDataBuilding.cpp +++ b/tests/TestGeoDataBuilding.cpp @@ -9,7 +9,7 @@ // #include -#include +#include "TestUtils.h" #include "GeoDataBuilding.h" #include "GeoDataMultiGeometry.h" @@ -22,6 +22,7 @@ private Q_SLOTS: void defaultConstructor(); + void testHeightExtraction(); }; void TestGeoDataBuilding::defaultConstructor() { @@ -62,6 +63,35 @@ QVERIFY(building2.multiGeometry()->size() > 0); } +void TestGeoDataBuilding::testHeightExtraction() +{ + QString const meters1 = "12 m"; + QString const meters2 = "12.8 meters"; + QString const meters3 = "12.56 meter"; + QString const meters4 = "14.44 metres"; + QString const meters5 = "23.43 metre"; + + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(meters1), 12.0, 0.0001); + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(meters2), 12.8, 0.0001); + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(meters3), 12.56, 0.0001); + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(meters4), 14.44, 0.0001); + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(meters5), 23.43, 0.0001); + + QString const feet1 = "55'4\""; // 664 inches + QString const feet2 = "60.56 feet"; // 726.72 inches + QString const feet3 = "300\'"; // 3600 inches + + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(feet1), 16.8656, 0.0001); + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(feet2), 18.4587, 0.0001); + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(feet3), 91.44, 0.0001); + + QString const unitless1 = "0.8"; // default in meters + QString const unitless2 = "12"; // default in meters + + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(unitless1), 0.8, 0.0001); + QFUZZYCOMPARE(GeoDataBuilding::parseBuildingHeight(unitless2), 12.0, 0.0001); +} + } QTEST_MAIN(Marble::TestGeoDataBuilding) 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,23 @@ 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); + GeoDataBuilding* newBuilding = new GeoDataBuilding(*building); + newBuilding->multiGeometry()->clear(); + newBuilding->multiGeometry()->append(ring); + placemark->setGeometry(newBuilding); + } else if (const auto prevPolygon = geodata_cast(&building->multiGeometry()->at(0))) { + GeoDataPolygon* poly = reducedPolygon(*prevPolygon, placemark, visualCategory); + GeoDataBuilding* newBuilding = new GeoDataBuilding(*building); + newBuilding->multiGeometry()->clear(); + newBuilding->multiGeometry()->append(poly); + placemark->setGeometry(newBuilding); } - placemark->setGeometry(reducedPolygon); } } } @@ -187,4 +189,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; +} + } diff --git a/tools/vectorosm-tilecreator/VectorClipper.h b/tools/vectorosm-tilecreator/VectorClipper.h --- a/tools/vectorosm-tilecreator/VectorClipper.h +++ b/tools/vectorosm-tilecreator/VectorClipper.h @@ -17,6 +17,8 @@ #include #include "GeoDataPlacemark.h" #include "GeoDataLinearRing.h" +#include "GeoDataBuilding.h" +#include "GeoDataMultiGeometry.h" #include #include #include @@ -50,7 +52,15 @@ void clipString(const GeoDataPlacemark *placemark, const ClipperLib::Path &tileBoundary, qreal minArea, GeoDataDocument* document, QSet &osmIds) { - const T* ring = static_cast(placemark->geometry()); + bool isBuilding = false; + T* ring; + if (const auto building = geodata_cast(placemark->geometry())) { + ring = geodata_cast(&building->multiGeometry()->at(0)); + isBuilding = true; + } else { + GeoDataPlacemark* copyPlacemark = new GeoDataPlacemark(*placemark); + ring = geodata_cast(copyPlacemark->geometry()); + } bool const isClosed = ring->isClosed() && canBeArea(placemark->visualCategory()); if (isClosed && minArea > 0.0 && area(*static_cast(ring)) < minArea) { return; @@ -92,7 +102,15 @@ ++index; } - newPlacemark->setGeometry(newRing); + if (isBuilding) { + const auto building = geodata_cast(placemark->geometry()); + GeoDataBuilding* newBuilding = new GeoDataBuilding(*building); + newBuilding->multiGeometry()->clear(); + newBuilding->multiGeometry()->append(newRing); + newPlacemark->setGeometry(newBuilding); + } else { + newPlacemark->setGeometry(newRing); + } if (placemark->osmData().id() > 0) { newPlacemark->osmData().addTag(QStringLiteral("mx:oid"), QString::number(placemark->osmData().id())); } diff --git a/tools/vectorosm-tilecreator/VectorClipper.cpp b/tools/vectorosm-tilecreator/VectorClipper.cpp --- a/tools/vectorosm-tilecreator/VectorClipper.cpp +++ b/tools/vectorosm-tilecreator/VectorClipper.cpp @@ -76,6 +76,12 @@ clipString(placemark, clip, minArea, tile, osmIds); } else if (geodata_cast(geometry)) { clipString(placemark, clip, minArea, tile, osmIds); + } else if (const auto building = geodata_cast(geometry)) { + if (geodata_cast(&building->multiGeometry()->at(0))) { + clipPolygon(placemark, clip, minArea, tile, osmIds); + } else if (geodata_cast(&building->multiGeometry()->at(0))) { + clipString(placemark, clip, minArea, tile, osmIds); + } } else { tile->append(placemark->clone()); osmIds << placemark->osmData().id(); @@ -224,7 +230,16 @@ void VectorClipper::clipPolygon(const GeoDataPlacemark *placemark, const ClipperLib::Path &tileBoundary, qreal minArea, GeoDataDocument *document, QSet &osmIds) { - const GeoDataPolygon* polygon = static_cast(placemark->geometry()); + bool isBuilding = false; + GeoDataPolygon* polygon; + if (const auto building = geodata_cast(placemark->geometry())) { + polygon = geodata_cast(&building->multiGeometry()->at(0)); + isBuilding = true; + } else { + GeoDataPlacemark* copyPlacemark = new GeoDataPlacemark(*placemark); + polygon = geodata_cast(copyPlacemark->geometry()); + } + if (minArea > 0.0 && area(polygon->outerBoundary()) < minArea) { return; } @@ -265,7 +280,15 @@ GeoDataPolygon* newPolygon = new GeoDataPolygon; newPolygon->setOuterBoundary(outerRing); - newPlacemark->setGeometry(newPolygon); + if (isBuilding) { + const auto building = geodata_cast(placemark->geometry()); + GeoDataBuilding* newBuilding = new GeoDataBuilding(*building); + newBuilding->multiGeometry()->clear(); + newBuilding->multiGeometry()->append(newPolygon); + newPlacemark->setGeometry(newBuilding); + } else { + newPlacemark->setGeometry(newPolygon); + } if (placemarkOsmData.id() > 0) { newPlacemarkOsmData.addTag(QStringLiteral("mx:oid"), QString::number(placemarkOsmData.id())); }