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 }