Marble

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

KDE's Doxygen guidelines are available online.