diff --git a/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.cpp b/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.cpp index fa8af0a3d..7a23303ed 100644 --- a/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.cpp +++ b/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.cpp @@ -1,454 +1,459 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2011 Konstantin Oblaukhov // #include "BuildingGeoPolygonGraphicsItem.h" #include "MarbleDebug.h" #include "ViewportParams.h" #include "GeoDataTypes.h" #include "GeoDataPlacemark.h" #include "GeoDataLinearRing.h" #include "GeoDataPolygon.h" #include "GeoDataPolyStyle.h" #include "OsmPlacemarkData.h" #include "GeoPainter.h" namespace Marble { BuildingGeoPolygonGraphicsItem::BuildingGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon) : AbstractGeoPolygonGraphicsItem(placemark, polygon) , m_buildingHeight(extractBuildingHeight(*placemark)) , m_buildingLabel(extractBuildingLabel(*placemark)) , m_entries(extractNamedEntries(*placemark)) + , m_hasInnerBoundaries(false) { setZValue(this->zValue() + m_buildingHeight); Q_ASSERT(m_buildingHeight > 0.0); QStringList paintLayers; paintLayers << QStringLiteral("Polygon/Building/frame") << QStringLiteral("Polygon/Building/roof"); setPaintLayers(paintLayers); } BuildingGeoPolygonGraphicsItem::BuildingGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing* ring) : AbstractGeoPolygonGraphicsItem(placemark, ring) , m_buildingHeight(extractBuildingHeight(*placemark)) , m_buildingLabel(extractBuildingLabel(*placemark)) , m_entries(extractNamedEntries(*placemark)) { setZValue(this->zValue() + m_buildingHeight); Q_ASSERT(m_buildingHeight > 0.0); QStringList paintLayers; paintLayers << QStringLiteral("Polygon/Building/frame") << QStringLiteral("Polygon/Building/roof"); setPaintLayers(paintLayers); } void BuildingGeoPolygonGraphicsItem::initializeBuildingPainting(const GeoPainter* painter, const ViewportParams *viewport, - bool &drawAccurate3D, bool &isCameraAboveBuilding, bool &hasInnerBoundaries, - QVector& outlinePolygons, - QVector& innerPolygons) const + bool &drawAccurate3D, bool &isCameraAboveBuilding ) const { drawAccurate3D = false; isCameraAboveBuilding = false; QPointF offsetAtCorner = buildingOffset(QPointF(0, 0), viewport, &isCameraAboveBuilding); qreal maxOffset = qMax( qAbs( offsetAtCorner.x() ), qAbs( offsetAtCorner.y() ) ); drawAccurate3D = painter->mapQuality() == HighQuality ? maxOffset > 5.0 : maxOffset > 8.0; +} +void BuildingGeoPolygonGraphicsItem::updatePolygons( const ViewportParams *viewport, + QVector& outlinePolygons, + 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 // to first paint the inner area with no pen and then the outlines with the correct pen. hasInnerBoundaries = polygon() ? !polygon()->innerBoundaries().isEmpty() : false; if (polygon()) { if (hasInnerBoundaries) { screenPolygons(viewport, polygon(), innerPolygons, outlinePolygons); } viewport->screenCoordinates(polygon()->outerBoundary(), outlinePolygons); } else if (ring()) { viewport->screenCoordinates(*ring(), outlinePolygons); } } QPointF BuildingGeoPolygonGraphicsItem::centroid(const QPolygonF &polygon, double &area) { auto centroid = QPointF(0.0, 0.0); area = 0.0; for (auto i=0, n=polygon.size(); i 0.0); qreal const buildingFactor = m_buildingHeight / EARTH_RADIUS; qreal const cameraHeightPixel = viewport->width() * cameraFactor; qreal buildingHeightPixel = viewport->radius() * buildingFactor; qreal const cameraDistance = cameraHeightPixel-buildingHeightPixel; if (isCameraAboveBuilding) { *isCameraAboveBuilding = cameraDistance > 0; } qreal const cc = cameraDistance * cameraHeightPixel; qreal const cb = cameraDistance * buildingHeightPixel; // The following lines calculate the same result, but are potentially slower due // to using more trigonometric method calls // qreal const alpha1 = atan2(offsetX, cameraHeightPixel); // qreal const alpha2 = atan2(offsetX, cameraHeightPixel-buildingHeightPixel); // qreal const shiftX = 2 * (cameraHeightPixel-buildingHeightPixel) * sin(0.5*(alpha2-alpha1)); qreal const offsetX = point.x() - viewport->width() / 2.0; qreal const offsetY = point.y() - viewport->height() / 2.0; qreal const shiftX = offsetX * cb / (cc + offsetX); qreal const shiftY = offsetY * cb / (cc + offsetY); 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) { if (layer.endsWith(QLatin1String("/frame"))) { + Q_ASSERT(!m_cachedOutlinePolygons.isEmpty()); + Q_ASSERT(!m_cachedInnerPolygons.isEmpty()); + updatePolygons(viewport, m_cachedOutlinePolygons, m_cachedInnerPolygons, m_hasInnerBoundaries); + if (m_cachedOutlinePolygons.isEmpty()) return; paintFrame(painter, viewport); } else if (layer.endsWith(QLatin1String("/roof"))) { + if (m_cachedOutlinePolygons.isEmpty()) return; paintRoof(painter, viewport); + qDeleteAll(m_cachedOutlinePolygons); + m_cachedOutlinePolygons.clear(); + m_cachedInnerPolygons.clear(); } else { mDebug() << "Didn't expect to have to paint layer " << layer << ", ignoring it."; } } void BuildingGeoPolygonGraphicsItem::paintRoof(GeoPainter* painter, const ViewportParams* viewport) { bool drawAccurate3D; bool isCameraAboveBuilding; - bool hasInnerBoundaries; - QVector outlinePolygons; - QVector innerPolygons; - initializeBuildingPainting(painter, viewport, drawAccurate3D, isCameraAboveBuilding, hasInnerBoundaries, outlinePolygons, innerPolygons); + initializeBuildingPainting(painter, viewport, drawAccurate3D, isCameraAboveBuilding); if (!isCameraAboveBuilding) { return; // do not render roof if we look inside the building } painter->save(); QPen const currentPen = configurePainter(painter, viewport); qreal maxSize(0.0); QPointF roofCenter; - if (hasInnerBoundaries) { + if (m_hasInnerBoundaries) { painter->setPen(Qt::NoPen); } // first paint the area (and the outline if there are no inner boundaries) double maxArea = 0.0; - foreach(QPolygonF* outlinePolygon, outlinePolygons) { + foreach(QPolygonF* outlinePolygon, m_cachedOutlinePolygons) { QRectF const boundingRect = outlinePolygon->boundingRect(); QPolygonF buildingRoof; if (!m_buildingLabel.isEmpty() || !m_entries.isEmpty()) { QSizeF const polygonSize = boundingRect.size(); qreal size = polygonSize.width() * polygonSize.height(); if (size > maxSize) { maxSize = size; double area; roofCenter = centroid(*outlinePolygon, area); maxArea = qMax(area, maxArea); roofCenter += buildingOffset(roofCenter, viewport); } } if ( drawAccurate3D) { buildingRoof.reserve(outlinePolygon->size()); foreach(const QPointF &point, *outlinePolygon) { buildingRoof << point + buildingOffset(point, viewport); } - if (hasInnerBoundaries) { + if (m_hasInnerBoundaries) { QRegion clip(buildingRoof.toPolygon()); - foreach(QPolygonF* innerPolygon, innerPolygons) { + foreach(QPolygonF* innerPolygon, m_cachedInnerPolygons) { QPolygonF buildingInner; buildingInner.reserve(innerPolygon->size()); foreach(const QPointF &point, *innerPolygon) { buildingInner << point + buildingOffset(point, viewport); } clip-=QRegion(buildingInner.toPolygon()); } painter->setClipRegion(clip); } painter->drawPolygon(buildingRoof); } else { QPointF const offset = buildingOffset(boundingRect.center(), viewport); painter->translate(offset); - if (hasInnerBoundaries) { + if (m_hasInnerBoundaries) { QRegion clip(outlinePolygon->toPolygon()); - foreach(QPolygonF* clipPolygon, innerPolygons) { + foreach(QPolygonF* clipPolygon, m_cachedInnerPolygons) { clip-=QRegion(clipPolygon->toPolygon()); } painter->setClipRegion(clip); } painter->drawPolygon(*outlinePolygon); painter->translate(-offset); } if (drawAccurate3D && !m_buildingLabel.isEmpty() && !roofCenter.isNull()) { double const w2 = 0.5 * painter->fontMetrics().width(m_buildingLabel); double const ascent = painter->fontMetrics().ascent(); double const descent = painter->fontMetrics().descent(); double const a2 = 0.5 * painter->fontMetrics().ascent(); QPointF const textPosition = roofCenter - QPointF(w2, -a2); if (buildingRoof.containsPoint(textPosition + QPointF(-2, -ascent), Qt::OddEvenFill) && buildingRoof.containsPoint(textPosition + QPointF(-2, descent), Qt::OddEvenFill) && buildingRoof.containsPoint(textPosition + QPointF(2+2*w2, descent), Qt::OddEvenFill) && buildingRoof.containsPoint(textPosition + QPointF(2+2*w2, -ascent), Qt::OddEvenFill) ) { painter->drawText(textPosition, m_buildingLabel); } } } // Render additional housenumbers at building entries if (!m_entries.isEmpty() && maxArea > 1600 * m_entries.size()) { QBrush brush = painter->brush(); QColor const brushColor = brush.color(); QColor lighterColor = brushColor.lighter(110); lighterColor.setAlphaF(0.9); brush.setColor(lighterColor); painter->setBrush(brush); foreach(const auto &entry, m_entries) { qreal x, y; viewport->screenCoordinates(entry.point, x, y); QPointF point(x, y); point += buildingOffset(point, viewport); auto const width = painter->fontMetrics().width(entry.label); auto const height = painter->fontMetrics().height(); QRectF rectangle(point, QSizeF(qMax(1.2*width, 1.1*height), 1.2*height)); rectangle.moveCenter(point); painter->drawRoundedRect(rectangle, 3, 3); painter->drawText(rectangle, Qt::AlignCenter, entry.label); } brush.setColor(brushColor); painter->setBrush(brush); } // then paint the outlines if there are inner boundaries - if (hasInnerBoundaries) { + if (m_hasInnerBoundaries) { painter->setPen(currentPen); - foreach(QPolygonF * polygon, outlinePolygons) { + foreach(QPolygonF * polygon, m_cachedOutlinePolygons) { QRectF const boundingRect = polygon->boundingRect(); if ( drawAccurate3D) { QPolygonF buildingRoof; buildingRoof.reserve(polygon->size()); foreach(const QPointF &point, *polygon) { buildingRoof << point + buildingOffset(point, viewport); } painter->drawPolyline(buildingRoof); } else { QPointF const offset = buildingOffset(boundingRect.center(), viewport); painter->translate(offset); painter->drawPolyline(*polygon); painter->translate(-offset); } } } - qDeleteAll(outlinePolygons); painter->restore(); } void BuildingGeoPolygonGraphicsItem::paintFrame(GeoPainter *painter, const ViewportParams *viewport) { // TODO: how does this match the Q_ASSERT in the constructor? if (m_buildingHeight == 0.0) { return; } if ((polygon() && !viewport->resolves(polygon()->outerBoundary().latLonAltBox(), 4)) || (ring() && !viewport->resolves(ring()->latLonAltBox(), 4))) { return; } painter->save(); bool drawAccurate3D; bool isCameraAboveBuilding; - bool hasInnerBoundaries; - QVector outlinePolygons; - QVector innerPolygons; - initializeBuildingPainting(painter, viewport, drawAccurate3D, isCameraAboveBuilding, hasInnerBoundaries, outlinePolygons, innerPolygons); + initializeBuildingPainting(painter, viewport, drawAccurate3D, isCameraAboveBuilding); configureFramePainter(painter); - foreach(QPolygonF* outlinePolygon, outlinePolygons) { + foreach(QPolygonF* outlinePolygon, m_cachedOutlinePolygons) { if (outlinePolygon->isEmpty()) { continue; } if ( drawAccurate3D && isCameraAboveBuilding ) { // draw the building sides int const size = outlinePolygon->size(); QPointF & a = (*outlinePolygon)[0]; QPointF shiftA = a + buildingOffset(a, viewport); for (int i=1; isetPen(QPen(painter->brush().color(), 1.5)); } painter->drawPolygon(buildingSide); a = b; shiftA = shiftB; } } else { // don't draw the building sides - just draw the base frame instead - if (hasInnerBoundaries) { + if (m_hasInnerBoundaries) { QRegion clip(outlinePolygon->toPolygon()); - foreach(QPolygonF* clipPolygon, innerPolygons) { + foreach(QPolygonF* clipPolygon, m_cachedInnerPolygons) { clip-=QRegion(clipPolygon->toPolygon()); } painter->setClipRegion(clip); } painter->drawPolygon(*outlinePolygon); } } - qDeleteAll(outlinePolygons); painter->restore(); } void BuildingGeoPolygonGraphicsItem::screenPolygons(const ViewportParams *viewport, const GeoDataPolygon *polygon, QVector &innerPolygons, QVector &outlines) { Q_ASSERT(polygon); QVector outerPolygons; viewport->screenCoordinates( polygon->outerBoundary(), outerPolygons ); outlines << outerPolygons; QVector innerBoundaries = polygon->innerBoundaries(); foreach (const GeoDataLinearRing &innerBoundary, innerBoundaries) { QVector innerPolygonsPerBoundary; viewport->screenCoordinates(innerBoundary, innerPolygonsPerBoundary); outlines << innerPolygonsPerBoundary; innerPolygons.reserve(innerPolygons.size() + innerPolygonsPerBoundary.size()); foreach( QPolygonF* innerPolygonPerBoundary, innerPolygonsPerBoundary ) { innerPolygons << innerPolygonPerBoundary; } } } void BuildingGeoPolygonGraphicsItem::configureFramePainter(GeoPainter *painter) const { GeoDataStyle::ConstPtr style = this->style(); if (!style) { painter->setPen( QPen() ); } else { const GeoDataPolyStyle& polyStyle = style->polyStyle(); const QColor transparentColor(Qt::transparent); QPen currentPen = painter->pen(); currentPen.setColor(transparentColor); painter->setPen(currentPen); if (!polyStyle.fill()) { painter->setBrush(transparentColor); } else { const QColor paintedColor = polyStyle.paintedColor(); painter->setBrush(paintedColor.darker(150)); } } } } diff --git a/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.h b/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.h index 8514cc589..c6793ba4d 100644 --- a/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.h +++ b/src/lib/marble/geodata/graphicsitem/BuildingGeoPolygonGraphicsItem.h @@ -1,61 +1,68 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2011 Konstantin Oblaukhov // #ifndef MARBLE_BUILDINGGEOPOLYGONGRAPHICSITEM_H #define MARBLE_BUILDINGGEOPOLYGONGRAPHICSITEM_H #include "AbstractGeoPolygonGraphicsItem.h" #include "GeoDataCoordinates.h" class QPointF; namespace Marble { class MARBLE_EXPORT BuildingGeoPolygonGraphicsItem : public AbstractGeoPolygonGraphicsItem { public: explicit BuildingGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon); explicit BuildingGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring); public: virtual void paint(GeoPainter* painter, const ViewportParams *viewport, const QString &layer); private: struct NamedEntry { GeoDataCoordinates point; QString label; }; void paintFrame(GeoPainter* painter, const ViewportParams *viewport); void paintRoof(GeoPainter* painter, const ViewportParams *viewport); void configureFramePainter(GeoPainter *painter) const; void initializeBuildingPainting(const GeoPainter* painter, const ViewportParams *viewport, - bool &drawAccurate3D, bool &isCameraAboveBuilding, bool &hasInnerBoundaries, - QVector& outlinePolygons, - QVector& innerPolygons) const; + bool &drawAccurate3D, bool &isCameraAboveBuilding) const; + void updatePolygons( const ViewportParams *viewport, + QVector& outlinePolygons, + QVector& innerPolygons, + bool &hasInnerBoundaries); + QPointF buildingOffset(const QPointF &point, const ViewportParams *viewport, bool* isCameraAboveBuilding = nullptr) const; static QPointF centroid(const QPolygonF &polygon, double &area); static void screenPolygons(const ViewportParams *viewport, const GeoDataPolygon* polygon, QVector &polygons, QVector &outlines); 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_buildingLabel; const QVector m_entries; + QVector m_cachedOutlinePolygons; + QVector m_cachedInnerPolygons; + bool m_hasInnerBoundaries; + }; } #endif diff --git a/src/lib/marble/geodata/graphicsitem/GeoLineStringGraphicsItem.cpp b/src/lib/marble/geodata/graphicsitem/GeoLineStringGraphicsItem.cpp index fed9c3f3f..1233c9471 100644 --- a/src/lib/marble/geodata/graphicsitem/GeoLineStringGraphicsItem.cpp +++ b/src/lib/marble/geodata/graphicsitem/GeoLineStringGraphicsItem.cpp @@ -1,324 +1,322 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2009 Andrew Manson // #include "GeoLineStringGraphicsItem.h" #include "GeoDataLineString.h" #include "GeoDataLineStyle.h" #include "GeoDataLabelStyle.h" #include "GeoDataPlacemark.h" #include "GeoDataPolyStyle.h" #include "GeoPainter.h" #include "StyleBuilder.h" #include "ViewportParams.h" #include "GeoDataStyle.h" #include "MarbleDebug.h" #include "MarbleMath.h" #include namespace Marble { GeoLineStringGraphicsItem::GeoLineStringGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLineString *lineString) : GeoGraphicsItem(placemark), m_lineString(lineString), m_renderLineString(lineString), m_renderLabel(false) { QString const category = StyleBuilder::visualCategoryName(placemark->visualCategory()); QStringList paintLayers; paintLayers << QLatin1String("LineString/") + category + QLatin1String("/outline"); paintLayers << QLatin1String("LineString/") + category + QLatin1String("/inline"); if (!feature()->name().isEmpty()) { paintLayers << QLatin1String("LineString/") + category + QLatin1String("/label"); } setPaintLayers(paintLayers); } void GeoLineStringGraphicsItem::setLineString( const GeoDataLineString* lineString ) { m_lineString = lineString; m_renderLineString = lineString; } const GeoDataLineString *GeoLineStringGraphicsItem::lineString() const { return m_lineString; } GeoDataLineString GeoLineStringGraphicsItem::merge(const QVector &lineStrings_) { if (lineStrings_.isEmpty()) { return GeoDataLineString(); } Q_ASSERT(!lineStrings_.isEmpty()); auto lineStrings = lineStrings_; GeoDataLineString result = *lineStrings.first(); lineStrings.pop_front(); for (bool matched = true; matched && !lineStrings.isEmpty();) { matched = false; for (auto lineString: lineStrings) { if (canMerge(result.first(), lineString->first())) { result.remove(0); result.reverse(); result << *lineString; lineStrings.removeOne(lineString); matched = true; break; } else if (canMerge(result.last(), lineString->first())) { result.remove(result.size()-1); result << *lineString; lineStrings.removeOne(lineString); matched = true; break; } else if (canMerge(result.first(), lineString->last())) { GeoDataLineString behind = result; result = *lineString; behind.remove(0); result << behind; lineStrings.removeOne(lineString); matched = true; break; } else if (canMerge(result.last(), lineString->last())) { GeoDataLineString behind = *lineString; behind.reverse(); behind.remove(0); result << behind; lineStrings.removeOne(lineString); matched = true; break; } } if (!matched) { return GeoDataLineString(); } } return lineStrings.isEmpty() ? result : GeoDataLineString(); } void GeoLineStringGraphicsItem::setMergedLineString(const GeoDataLineString &mergedLineString) { m_mergedLineString = mergedLineString; m_renderLineString = mergedLineString.isEmpty() ? m_lineString : &m_mergedLineString; } const GeoDataLatLonAltBox& GeoLineStringGraphicsItem::latLonAltBox() const { return m_renderLineString->latLonAltBox(); } void GeoLineStringGraphicsItem::paint(GeoPainter* painter, const ViewportParams* viewport , const QString &layer) { int const tileLevel = qLn( viewport->radius() / 64.0 ) / qLn( 2.0 ); setRenderContext(RenderContext(tileLevel)); if (layer.endsWith(QLatin1String("/outline"))) { - qDeleteAll(m_cachedPolygons); - m_cachedPolygons.clear(); + Q_ASSERT(!m_cachedPolygons.isEmpty()); painter->polygonsFromLineString(*m_renderLineString, m_cachedPolygons); if (m_cachedPolygons.empty()) { return; } if (painter->mapQuality() == HighQuality || painter->mapQuality() == PrintQuality) { paintOutline(painter, viewport); } } else if (layer.endsWith(QLatin1String("/inline"))) { if (m_cachedPolygons.empty()) { return; } paintInline(painter, viewport); } else if (layer.endsWith(QLatin1String("/label"))) { if (!m_cachedPolygons.empty()) { if (m_renderLabel) { paintLabel(painter, viewport); } } qDeleteAll(m_cachedPolygons); m_cachedPolygons.clear(); } else { - qDeleteAll(m_cachedPolygons); - m_cachedPolygons.clear(); + Q_ASSERT(!m_cachedPolygons.isEmpty()); painter->polygonsFromLineString(*m_renderLineString, m_cachedPolygons); if (m_cachedPolygons.empty()) { return; } foreach(const QPolygonF* itPolygon, m_cachedPolygons) { painter->drawPolyline(*itPolygon); } qDeleteAll(m_cachedPolygons); m_cachedPolygons.clear(); } } void GeoLineStringGraphicsItem::paintInline(GeoPainter* painter, const ViewportParams* viewport) { if ( ( !viewport->resolves( m_renderLineString->latLonAltBox(), 2) ) ) { return; } painter->save(); LabelPositionFlags labelPositionFlags = NoLabel; QPen currentPen = configurePainter(painter, viewport, labelPositionFlags); GeoDataStyle::ConstPtr style = this->style(); const GeoDataLineStyle& lineStyle = style->lineStyle(); if (lineStyle.cosmeticOutline() && lineStyle.penStyle() == Qt::SolidLine) { const float currentPenWidth = currentPen.widthF(); if (currentPenWidth > 2.5f) { currentPen.setWidthF(currentPenWidth - 2.0f); } currentPen.setColor(style->polyStyle().paintedColor()); painter->setPen( currentPen ); } foreach(const QPolygonF* itPolygon, m_cachedPolygons) { painter->drawPolyline(*itPolygon); } painter->restore(); } void GeoLineStringGraphicsItem::paintOutline(GeoPainter *painter, const ViewportParams *viewport) { if ( ( !viewport->resolves( m_renderLineString->latLonAltBox(), 2) ) ) { return; } painter->save(); LabelPositionFlags labelPositionFlags = NoLabel; QPen currentPen = configurePainter(painter, viewport, labelPositionFlags); m_renderLabel = !( currentPen.widthF() < 8.0f ); if (!( currentPen.widthF() < 2.5f )) { foreach(const QPolygonF* itPolygon, m_cachedPolygons) { painter->drawPolyline(*itPolygon); } } painter->restore(); } void GeoLineStringGraphicsItem::paintLabel(GeoPainter *painter, const ViewportParams *viewport) { if ( ( !viewport->resolves( m_renderLineString->latLonAltBox(), 2) ) ) { return; } painter->save(); LabelPositionFlags labelPositionFlags = NoLabel; QPen currentPen = configurePainter(painter, viewport, labelPositionFlags); GeoDataStyle::ConstPtr style = this->style(); const GeoDataLabelStyle& labelStyle = style->labelStyle(); // Activate the lines below to paint a label background which // prevents antialiasing overpainting glitches, but leads to // other glitches. //QColor const color = style->polyStyle().paintedColor(); //painter->setBackground(QBrush(color)); //painter->setBackgroundMode(Qt::OpaqueMode); painter->drawLabelsForPolygons(m_cachedPolygons, feature()->name(), FollowLine, labelStyle.paintedColor()); painter->restore(); } QPen GeoLineStringGraphicsItem::configurePainter(GeoPainter *painter, const ViewportParams *viewport, LabelPositionFlags &labelPositionFlags) const { QPen currentPen = painter->pen(); GeoDataStyle::ConstPtr style = this->style(); if (!style) { painter->setPen( QPen() ); } else { const GeoDataLineStyle& lineStyle = style->lineStyle(); const QColor linePaintedColor = lineStyle.paintedColor(); if (currentPen.color() != linePaintedColor) { currentPen.setColor(linePaintedColor); } const float lineWidth = lineStyle.width(); const float linePhysicalWidth = lineStyle.physicalWidth(); if (currentPen.widthF() != lineWidth || linePhysicalWidth != 0.0) { const float scaledLinePhysicalWidth = float(viewport->radius()) / EARTH_RADIUS * linePhysicalWidth; if (scaledLinePhysicalWidth < lineWidth) { currentPen.setWidthF(lineWidth); } else { currentPen.setWidthF(scaledLinePhysicalWidth); } } else if (lineWidth != 0.0 ) { currentPen.setWidthF(lineWidth); } const Qt::PenCapStyle lineCapStyle = lineStyle.capStyle(); if (currentPen.capStyle() != lineCapStyle) { currentPen.setCapStyle(lineCapStyle); } if (painter->mapQuality() == HighQuality || painter->mapQuality() == PrintQuality) { const Qt::PenStyle linePenStyle = lineStyle.penStyle(); if (currentPen.style() != linePenStyle) { currentPen.setStyle(linePenStyle); } if (linePenStyle == Qt::CustomDashLine) { currentPen.setDashPattern(lineStyle.dashPattern()); } } else { currentPen.setStyle(Qt::SolidLine); } if ( painter->mapQuality() != Marble::HighQuality && painter->mapQuality() != Marble::PrintQuality ) { QColor penColor = currentPen.color(); penColor.setAlpha( 255 ); currentPen.setColor( penColor ); } if ( painter->pen() != currentPen ) painter->setPen( currentPen ); if (lineStyle.background()) { QBrush brush = painter->background(); brush.setColor(style->polyStyle().paintedColor()); painter->setBackground( brush ); painter->setBackgroundMode( Qt::OpaqueMode ); } // label styles const GeoDataLabelStyle& labelStyle = style->labelStyle(); painter->setFont(labelStyle.font() ); switch (labelStyle.alignment()) { case GeoDataLabelStyle::Corner: case GeoDataLabelStyle::Right: labelPositionFlags |= LineStart; break; case GeoDataLabelStyle::Center: labelPositionFlags |= LineCenter; break; } } return currentPen; } bool GeoLineStringGraphicsItem::canMerge(const GeoDataCoordinates &a, const GeoDataCoordinates &b) { return distanceSphere(a, b) * EARTH_RADIUS < 0.1; } }