File indexing completed on 2025-01-05 03:59:02

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