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 }