File indexing completed on 2024-05-19 03:51:39

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2011 Konstantin Oblaukhov <oblaukhov.konstantin@gmail.com>
0004 //
0005 
0006 #include "AbstractGeoPolygonGraphicsItem.h"
0007 
0008 #include "GeoDataLinearRing.h"
0009 #include "GeoDataPolygon.h"
0010 #include "GeoDataBuilding.h"
0011 #include "GeoPainter.h"
0012 #include "GeoDataLatLonAltBox.h"
0013 #include "GeoDataStyle.h"
0014 #include "GeoDataIconStyle.h"
0015 #include "GeoDataLineStyle.h"
0016 #include "GeoDataPlacemark.h"
0017 #include "GeoDataPolyStyle.h"
0018 #include "OsmPlacemarkData.h"
0019 #include "MarbleDebug.h"
0020 #include "ViewportParams.h"
0021 
0022 #include <QtMath>
0023 #include <QImageReader>
0024 #include <QPixmapCache>
0025 
0026 namespace Marble
0027 {
0028 
0029 const void *AbstractGeoPolygonGraphicsItem::s_previousStyle = nullptr;
0030 
0031 AbstractGeoPolygonGraphicsItem::AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon) :
0032     GeoGraphicsItem(placemark),
0033     m_polygon(polygon),
0034     m_ring(nullptr),
0035     m_building(nullptr)
0036 {
0037 }
0038 
0039 AbstractGeoPolygonGraphicsItem::AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring) :
0040     GeoGraphicsItem(placemark),
0041     m_polygon(nullptr),
0042     m_ring(ring),
0043     m_building(nullptr)
0044 {
0045 }
0046 
0047 AbstractGeoPolygonGraphicsItem::AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building) :
0048     GeoGraphicsItem(placemark),
0049     m_polygon(nullptr),
0050     m_ring(nullptr),
0051     m_building(building)
0052 {
0053 }
0054 
0055 AbstractGeoPolygonGraphicsItem::~AbstractGeoPolygonGraphicsItem()
0056 {
0057 }
0058 
0059 const GeoDataLatLonAltBox& AbstractGeoPolygonGraphicsItem::latLonAltBox() const
0060 {
0061     if(m_polygon) {
0062         return m_polygon->latLonAltBox();
0063     } else if (m_ring) {
0064         return m_ring->latLonAltBox();
0065     }
0066 
0067     return m_building->latLonAltBox();
0068 }
0069 
0070 void AbstractGeoPolygonGraphicsItem::paint( GeoPainter* painter, const ViewportParams* viewport, const QString &layer, int tileZoomLevel)
0071 {
0072     Q_UNUSED(layer);
0073     Q_UNUSED(tileZoomLevel);
0074 
0075     bool isValid = true;
0076     if (s_previousStyle != style().data()) {
0077         isValid = configurePainter(painter, *viewport);
0078     }
0079     s_previousStyle = style().data();
0080 
0081     if (!isValid) return;
0082 
0083     if ( m_polygon ) {
0084         bool innerResolved = false;
0085 
0086         for(auto const & ring : m_polygon->innerBoundaries()) {
0087             if (viewport->resolves(ring.latLonAltBox(), 4)) {
0088                innerResolved = true;
0089                break;
0090             }
0091         }
0092 
0093         if (innerResolved) {
0094             painter->drawPolygon(*m_polygon);
0095         }
0096         else {
0097             painter->drawPolygon(m_polygon->outerBoundary());
0098         }
0099     } else if ( m_ring ) {
0100         painter->drawPolygon( *m_ring );
0101     }
0102 }
0103 
0104 bool AbstractGeoPolygonGraphicsItem::contains(const QPoint &screenPosition, const ViewportParams *viewport) const
0105 {
0106     auto const visualCategory = static_cast<const GeoDataPlacemark*>(feature())->visualCategory();
0107     if (visualCategory == GeoDataPlacemark::Landmass ||
0108             visualCategory == GeoDataPlacemark::UrbanArea ||
0109             (visualCategory >= GeoDataPlacemark::LanduseAllotments && visualCategory <= GeoDataPlacemark::LanduseVineyard)) {
0110         return false;
0111     }
0112 
0113     double lon, lat;
0114     viewport->geoCoordinates(screenPosition.x(), screenPosition.y(), lon, lat, GeoDataCoordinates::Radian);
0115     auto const coordinates = GeoDataCoordinates(lon, lat);
0116     if (m_polygon) {
0117         return m_polygon->contains(coordinates);
0118     } else if (m_ring) {
0119         return m_ring->contains(coordinates);
0120     }
0121     return false;
0122 }
0123 
0124 bool AbstractGeoPolygonGraphicsItem::configurePainter(GeoPainter *painter, const ViewportParams &viewport) const
0125 {
0126     QPen currentPen = painter->pen();
0127     GeoDataStyle::ConstPtr style = this->style();
0128     if (!style) {
0129         painter->setPen( QPen() ); // "style-less" polygons: a 1px black solid line
0130     }
0131     else {
0132         const GeoDataPolyStyle& polyStyle = style->polyStyle();
0133 
0134         if (polyStyle.outline()) {
0135             const GeoDataLineStyle& lineStyle = style->lineStyle();
0136 
0137             // To save performance we avoid making changes to the painter's pen.
0138             // So we first take a copy of the actual painter pen, make changes to it
0139             // and only if the resulting pen is different from the actual pen
0140             // we replace the painter's pen with our new pen.
0141 
0142             // We want to avoid the mandatory detach in QPen::setColor(),
0143             // so we carefully check whether applying the setter is needed
0144             currentPen.setColor(lineStyle.paintedColor());
0145             currentPen.setWidthF(lineStyle.width());
0146             currentPen.setCapStyle(lineStyle.capStyle());
0147             currentPen.setStyle(lineStyle.penStyle());
0148 
0149             if (painter->pen().color() != currentPen.color()) {
0150                 painter->setPen(currentPen);
0151             }
0152         }
0153         else {
0154             // polygons without outline: Qt::NoPen (not drawn)
0155             if (currentPen.style() != Qt::NoPen) {
0156                 painter->setPen(Qt::NoPen);
0157             }
0158         }
0159 
0160         if (!polyStyle.fill()) {            
0161             painter->setBrush(Qt::transparent);
0162         }
0163         else {
0164             const QColor paintedColor = polyStyle.paintedColor();
0165             if (painter->brush().color() != paintedColor ||
0166                 painter->brush().style() != polyStyle.brushStyle()) {
0167                 if (!polyStyle.texturePath().isEmpty() || !polyStyle.textureImage().isNull()) {
0168                     GeoDataCoordinates coords = latLonAltBox().center();
0169                     qreal x, y;
0170                     viewport.screenCoordinates(coords, x, y);
0171                     QBrush brush(texture(polyStyle.texturePath(), paintedColor));
0172                     painter->setBrush(brush);
0173                     painter->setBrushOrigin(QPoint(x,y));
0174                 }
0175                 else {
0176                     painter->setBrush(QBrush(paintedColor, polyStyle.brushStyle()));
0177                 }
0178             }
0179         }
0180     }
0181 
0182     return true;
0183 }
0184 
0185 int AbstractGeoPolygonGraphicsItem::extractElevation(const GeoDataPlacemark &placemark)
0186 {
0187     int elevation = 0;
0188 
0189     const OsmPlacemarkData &osmData = placemark.osmData();
0190 
0191     const auto tagIter = osmData.findTag(QStringLiteral("ele"));
0192     if (tagIter != osmData.tagsEnd()) {
0193         elevation = tagIter.value().toInt();
0194     }
0195 
0196     return elevation;
0197 }
0198 
0199 QPixmap AbstractGeoPolygonGraphicsItem::texture(const QString &texturePath, const QColor &color) const
0200 {
0201     QString const key = QString::number(color.rgba()) + '/' + texturePath;
0202     QPixmap texture;
0203     if (!QPixmapCache::find(key, &texture)) {
0204         QImageReader imageReader(style()->polyStyle().resolvePath(texturePath));
0205         texture = QPixmap::fromImageReader(&imageReader);
0206 
0207         if (texture.hasAlphaChannel()) {
0208             QPixmap pixmap (texture.size());
0209             pixmap.fill(color);
0210             QPainter imagePainter(&pixmap);
0211             imagePainter.drawPixmap(0, 0, texture);
0212             imagePainter.end();
0213             texture = pixmap;
0214         }
0215         QPixmapCache::insert(key, texture);
0216     }
0217     return texture;
0218 }
0219 
0220 void AbstractGeoPolygonGraphicsItem::setLinearRing(GeoDataLinearRing *ring)
0221 {
0222     Q_ASSERT(m_building);
0223     Q_ASSERT(!m_polygon);
0224     m_ring = ring;
0225 }
0226 
0227 void AbstractGeoPolygonGraphicsItem::setPolygon(GeoDataPolygon *polygon)
0228 {
0229     Q_ASSERT(m_building);
0230     Q_ASSERT(!m_ring);
0231     m_polygon = polygon;
0232 }
0233 
0234 }