Marble

AbstractGeoPolygonGraphicsItem.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2011 Konstantin Oblaukhov <oblaukhov.konstantin@gmail.com>
4//
5
6#include "AbstractGeoPolygonGraphicsItem.h"
7
8#include "GeoDataBuilding.h"
9#include "GeoDataIconStyle.h"
10#include "GeoDataLatLonAltBox.h"
11#include "GeoDataLineStyle.h"
12#include "GeoDataLinearRing.h"
13#include "GeoDataPlacemark.h"
14#include "GeoDataPolyStyle.h"
15#include "GeoDataPolygon.h"
16#include "GeoDataStyle.h"
17#include "GeoPainter.h"
18#include "MarbleDebug.h"
19#include "OsmPlacemarkData.h"
20#include "ViewportParams.h"
21
22#include <QImageReader>
23#include <QPixmapCache>
24#include <QtMath>
25
26namespace Marble
27{
28
29const void *AbstractGeoPolygonGraphicsItem::s_previousStyle = nullptr;
30
31AbstractGeoPolygonGraphicsItem::AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataPolygon *polygon)
32 : GeoGraphicsItem(placemark)
33 , m_polygon(polygon)
34 , m_ring(nullptr)
35 , m_building(nullptr)
36{
37}
38
39AbstractGeoPolygonGraphicsItem::AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataLinearRing *ring)
40 : GeoGraphicsItem(placemark)
41 , m_polygon(nullptr)
42 , m_ring(ring)
43 , m_building(nullptr)
44{
45}
46
47AbstractGeoPolygonGraphicsItem::AbstractGeoPolygonGraphicsItem(const GeoDataPlacemark *placemark, const GeoDataBuilding *building)
48 : GeoGraphicsItem(placemark)
49 , m_polygon(nullptr)
50 , m_ring(nullptr)
51 , m_building(building)
52{
53}
54
55AbstractGeoPolygonGraphicsItem::~AbstractGeoPolygonGraphicsItem() = default;
56
57const GeoDataLatLonAltBox &AbstractGeoPolygonGraphicsItem::latLonAltBox() const
58{
59 if (m_polygon) {
60 return m_polygon->latLonAltBox();
61 } else if (m_ring) {
62 return m_ring->latLonAltBox();
63 }
64
65 return m_building->latLonAltBox();
66}
67
68void AbstractGeoPolygonGraphicsItem::paint(GeoPainter *painter, const ViewportParams *viewport, const QString &layer, int tileZoomLevel)
69{
70 Q_UNUSED(layer);
71 Q_UNUSED(tileZoomLevel);
72
73 bool isValid = true;
74 if (s_previousStyle != style().data()) {
75 isValid = configurePainter(painter, *viewport);
76 }
77 s_previousStyle = style().data();
78
79 if (!isValid)
80 return;
81
82 if (m_polygon) {
83 bool innerResolved = false;
84
85 for (auto const &ring : m_polygon->innerBoundaries()) {
86 if (viewport->resolves(ring.latLonAltBox(), 4)) {
87 innerResolved = true;
88 break;
89 }
90 }
91
92 if (innerResolved) {
93 painter->drawPolygon(*m_polygon);
94 } else {
95 painter->drawPolygon(m_polygon->outerBoundary());
96 }
97 } else if (m_ring) {
98 painter->drawPolygon(*m_ring);
99 }
100}
101
102bool AbstractGeoPolygonGraphicsItem::contains(const QPoint &screenPosition, const ViewportParams *viewport) const
103{
104 auto const visualCategory = static_cast<const GeoDataPlacemark *>(feature())->visualCategory();
105 if (visualCategory == GeoDataPlacemark::Landmass || visualCategory == GeoDataPlacemark::UrbanArea
106 || (visualCategory >= GeoDataPlacemark::LanduseAllotments && visualCategory <= GeoDataPlacemark::LanduseVineyard)) {
107 return false;
108 }
109
110 double lon, lat;
111 viewport->geoCoordinates(screenPosition.x(), screenPosition.y(), lon, lat, GeoDataCoordinates::Radian);
112 auto const coordinates = GeoDataCoordinates(lon, lat);
113 if (m_polygon) {
114 return m_polygon->contains(coordinates);
115 } else if (m_ring) {
116 return m_ring->contains(coordinates);
117 }
118 return false;
119}
120
121bool AbstractGeoPolygonGraphicsItem::configurePainter(GeoPainter *painter, const ViewportParams &viewport) const
122{
123 QPen currentPen = painter->pen();
124 GeoDataStyle::ConstPtr style = this->style();
125 if (!style) {
126 painter->setPen(QPen()); // "style-less" polygons: a 1px black solid line
127 } else {
128 const GeoDataPolyStyle &polyStyle = style->polyStyle();
129
130 if (polyStyle.outline()) {
131 const GeoDataLineStyle &lineStyle = style->lineStyle();
132
133 // To save performance we avoid making changes to the painter's pen.
134 // So we first take a copy of the actual painter pen, make changes to it
135 // and only if the resulting pen is different from the actual pen
136 // we replace the painter's pen with our new pen.
137
138 // We want to avoid the mandatory detach in QPen::setColor(),
139 // so we carefully check whether applying the setter is needed
140 currentPen.setColor(lineStyle.paintedColor());
141 currentPen.setWidthF(lineStyle.width());
142 currentPen.setCapStyle(lineStyle.capStyle());
143 currentPen.setStyle(lineStyle.penStyle());
144
145 if (painter->pen().color() != currentPen.color()) {
146 painter->setPen(currentPen);
147 }
148 } else {
149 // polygons without outline: Qt::NoPen (not drawn)
150 if (currentPen.style() != Qt::NoPen) {
151 painter->setPen(Qt::NoPen);
152 }
153 }
154
155 if (!polyStyle.fill()) {
156 painter->setBrush(Qt::transparent);
157 } else {
158 const QColor paintedColor = polyStyle.paintedColor();
159 if (painter->brush().color() != paintedColor || painter->brush().style() != polyStyle.brushStyle()) {
160 if (!polyStyle.texturePath().isEmpty() || !polyStyle.textureImage().isNull()) {
161 GeoDataCoordinates coords = latLonAltBox().center();
162 qreal x, y;
163 viewport.screenCoordinates(coords, x, y);
164 QBrush brush(texture(polyStyle.texturePath(), paintedColor));
165 painter->setBrush(brush);
166 painter->setBrushOrigin(QPoint(x, y));
167 } else {
168 painter->setBrush(QBrush(paintedColor, polyStyle.brushStyle()));
169 }
170 }
171 }
172 }
173
174 return true;
175}
176
177int AbstractGeoPolygonGraphicsItem::extractElevation(const GeoDataPlacemark &placemark)
178{
179 int elevation = 0;
180
181 const OsmPlacemarkData &osmData = placemark.osmData();
182
183 const auto tagIter = osmData.findTag(QStringLiteral("ele"));
184 if (tagIter != osmData.tagsEnd()) {
185 elevation = tagIter.value().toInt();
186 }
187
188 return elevation;
189}
190
191QPixmap AbstractGeoPolygonGraphicsItem::texture(const QString &texturePath, const QColor &color) const
192{
193 QString const key = QString::number(color.rgba()) + QLatin1Char('/') + texturePath;
194 QPixmap texture;
195 if (!QPixmapCache::find(key, &texture)) {
196 QImageReader imageReader(style()->polyStyle().resolvePath(texturePath));
197 texture = QPixmap::fromImageReader(&imageReader);
198
199 if (texture.hasAlphaChannel()) {
200 QPixmap pixmap(texture.size());
201 pixmap.fill(color);
202 QPainter imagePainter(&pixmap);
203 imagePainter.drawPixmap(0, 0, texture);
204 imagePainter.end();
205 texture = pixmap;
206 }
207 QPixmapCache::insert(key, texture);
208 }
209 return texture;
210}
211
212void AbstractGeoPolygonGraphicsItem::setLinearRing(GeoDataLinearRing *ring)
213{
214 Q_ASSERT(m_building);
215 Q_ASSERT(!m_polygon);
216 m_ring = ring;
217}
218
219void AbstractGeoPolygonGraphicsItem::setPolygon(GeoDataPolygon *polygon)
220{
221 Q_ASSERT(m_building);
222 Q_ASSERT(!m_ring);
223 m_polygon = polygon;
224}
225
226}
This file contains the headers for ViewportParams.
bool isValid(QStringView ifopt)
Binds a QML item to a specific geodetic location in screen coordinates.
QRgb rgba() const const
QColor color() const const
void setCapStyle(Qt::PenCapStyle style)
void setColor(const QColor &color)
void setStyle(Qt::PenStyle style)
void setWidthF(qreal width)
Qt::PenStyle style() const const
QPixmap fromImageReader(QImageReader *imageReader, Qt::ImageConversionFlags flags)
bool hasAlphaChannel() const const
QSize size() const const
bool find(const Key &key, QPixmap *pixmap)
Key insert(const QPixmap &pixmap)
int x() const const
int y() const const
QString number(double n, char format, int precision)
transparent
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:15:45 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.