Marble

GeoDataIconStyle.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2007 Murad Tagirov <[email protected]>
4 //
5 
6 
7 #include "GeoDataIconStyle.h"
8 
9 #include <QUrl>
10 
11 #include "MarbleDirs.h"
12 #include "MarbleDebug.h"
13 #include "RemoteIconLoader.h"
14 
15 #include "GeoDataTypes.h"
16 
17 #include <QImageReader>
18 
19 namespace Marble
20 {
21 
22 class GeoDataIconStylePrivate
23 {
24  public:
25  GeoDataIconStylePrivate()
26  : m_scale( 1.0 ),
27  m_size(0, 0),
28  m_aspectRatioMode(Qt::KeepAspectRatio),
29  m_iconPath(),
30  m_heading( 0 )
31  {
32  }
33 
34  GeoDataIconStylePrivate( const QString& iconPath, const QPointF &hotSpot )
35  : m_scale( 1.0 ),
36  m_size(0, 0),
37  m_aspectRatioMode(Qt::KeepAspectRatio),
38  m_iconPath( iconPath ),
39  m_hotSpot( hotSpot ),
40  m_heading( 0 )
41  {
42  }
43 
44  RemoteIconLoader* remoteIconLoader() const
45  {
46  static RemoteIconLoader *remoteIconLoader = new RemoteIconLoader();
47  return remoteIconLoader;
48  }
49 
50  QSize scaledSize(const QSize &size) const
51  {
52  QSize iconSize = size.isNull() ? m_icon.size() : size;
53  // Scaling the placemark's icon based on its size, scale, and maximum icon size.
54  if ( iconSize.width()*m_scale > s_maximumIconSize.width()
55  || iconSize.height()*m_scale > s_maximumIconSize.height() ) {
56  iconSize.scale( s_maximumIconSize, Qt::KeepAspectRatio );
57  }
58  else if ( iconSize.width()*m_scale < s_minimumIconSize.width()
59  || iconSize.height()*m_scale < s_minimumIconSize.width() ) {
60  iconSize.scale( s_minimumIconSize, Qt::KeepAspectRatio );
61  }
62  else {
63  iconSize *= m_scale;
64  }
65 
66  return QSize(iconSize.width() - iconSize.width() % 2,
67  iconSize.height() - iconSize.height() % 2);
68  }
69 
70  QImage loadIcon(const QString &path, const QSize &size) const
71  {
72  if (!path.isEmpty()) {
73  // Icons from the local file system
74  if (!size.isNull()) {
75  QImageReader imageReader;
76  imageReader.setFileName(path);
77  auto const imageSize = imageReader.size();
78  auto const finalSize = imageSize.scaled(size, m_aspectRatioMode);
79  imageReader.setScaledSize(finalSize);
80  QImage icon = imageReader.read();
81  if (icon.isNull()) {
82  mDebug() << "GeoDataIconStyle: Failed to read image " << path << ": " << imageReader.errorString();
83  }
84  return icon;
85  }
86  QImage icon = QImage(path);
87  if (!icon.isNull()) {
88  return icon;
89  }
90  }
91 
92  if(QUrl(m_iconPath).isValid() ) {
93  // if image is not found on disk, check whether the icon is
94  // at remote location. If yes then go for remote icon loading
95  return remoteIconLoader()->load(QUrl(m_iconPath));
96  }
97 
98  mDebug() << "Unable to open style icon at: " << path;
99  return QImage();
100  }
101 
102  float m_scale;
103 
104  QImage m_icon;
105  QSize m_size;
106  Qt::AspectRatioMode m_aspectRatioMode;
107  QImage m_scaledIcon;
108  QString m_iconPath;
109  GeoDataHotSpot m_hotSpot;
110  int m_heading;
111 };
112 
113 GeoDataIconStyle::GeoDataIconStyle() :
114  d( new GeoDataIconStylePrivate() )
115 {
116 }
117 
118 GeoDataIconStyle::GeoDataIconStyle( const GeoDataIconStyle& other ) :
119  GeoDataColorStyle( other ), d( new GeoDataIconStylePrivate( *other.d ) )
120 {
121 }
122 
123 GeoDataIconStyle::GeoDataIconStyle( const QString& iconPath, const QPointF &hotSpot ) :
124  d( new GeoDataIconStylePrivate( iconPath, hotSpot ) )
125 {
126 }
127 
128 GeoDataIconStyle::~GeoDataIconStyle()
129 {
130  delete d;
131 }
132 
133 GeoDataIconStyle& GeoDataIconStyle::operator=( const GeoDataIconStyle& other )
134 {
136  *d = *other.d;
137  return *this;
138 }
139 
140 bool GeoDataIconStyle::operator==( const GeoDataIconStyle &other ) const
141 {
142  if ( GeoDataColorStyle::operator!=( other ) ) {
143  return false;
144  }
145 
146  return d->m_scale == other.d->m_scale &&
147  d->m_icon == other.d->m_icon &&
148  d->m_size == other.d->m_size &&
149  d->m_iconPath == other.d->m_iconPath &&
150  d->m_hotSpot == other.d->m_hotSpot &&
151  d->m_heading == other.d->m_heading;
152 }
153 
154 bool GeoDataIconStyle::operator!=( const GeoDataIconStyle &other ) const
155 {
156  return !this->operator==( other );
157 }
158 
159 const char* GeoDataIconStyle::nodeType() const
160 {
161  return GeoDataTypes::GeoDataIconStyleType;
162 }
163 
164 void GeoDataIconStyle::setIcon(const QImage &icon)
165 {
166  d->m_icon = icon;
167  d->m_scaledIcon = QImage();
168 }
169 
170 void GeoDataIconStyle::setIconPath( const QString& filename )
171 {
172  d->m_iconPath = filename;
173 
174  /**
175  * Set the m_icon to be a default-constructed icon
176  * so that m_icon is null and icon() doesn't return
177  * previously loaded icon.
178  */
179  d->m_icon = QImage();
180  d->m_scaledIcon = QImage();
181 }
182 
183 QString GeoDataIconStyle::iconPath() const
184 {
185  return d->m_iconPath;
186 }
187 
188 QImage GeoDataIconStyle::icon() const
189 {
190  if ( !d->m_icon.isNull() ) {
191  return d->m_icon;
192  }
193  else if ( !d->m_iconPath.isEmpty() ) {
194  d->m_icon = d->loadIcon(resolvePath(d->m_iconPath), d->m_size);
195  return d->m_icon;
196  }
197  else
198  return QImage();
199 }
200 
201 void GeoDataIconStyle::setHotSpot( const QPointF& hotSpot,
202  GeoDataHotSpot::Units xunits,
203  GeoDataHotSpot::Units yunits )
204 {
205  d->m_hotSpot.setHotSpot( hotSpot, xunits, yunits );
206 }
207 
208 QPointF GeoDataIconStyle::hotSpot( GeoDataHotSpot::Units &xunits, GeoDataHotSpot::Units &yunits ) const
209 {
210  return d->m_hotSpot.hotSpot( xunits, yunits );
211 }
212 
213 void GeoDataIconStyle::setSize(const QSize &size, Qt::AspectRatioMode aspectRatioMode)
214 {
215  if (size == d->m_size && aspectRatioMode == d->m_aspectRatioMode) {
216  return;
217  }
218 
219  d->m_aspectRatioMode = aspectRatioMode;
220  d->m_size = QSize(size.width() - size.width() % 2, size.height() - size.height() % 2);
221  if (!d->m_size.isNull() && !d->m_icon.isNull()) {
222  // Resize existing icon that cannot be restored from an image path
223  d->m_icon = d->m_icon.scaled(d->m_size);
224  } else if (!d->m_iconPath.isEmpty()) {
225  // Lazily reload the icons
226  d->m_icon = QImage();
227  d->m_scaledIcon = QImage();
228  }
229 }
230 
231 QSize GeoDataIconStyle::size() const
232 {
233  return d->m_size;
234 }
235 
236 void GeoDataIconStyle::setScale(float scale)
237 {
238  d->m_scale = scale;
239  d->m_scaledIcon = QImage();
240 }
241 
242 float GeoDataIconStyle::scale() const
243 {
244  return d->m_scale;
245 }
246 
247 QImage GeoDataIconStyle::scaledIcon() const
248 {
249  if (!d->m_scaledIcon.isNull()) {
250  return d->m_scaledIcon;
251  }
252 
253  // Invalid or trivial scale
254  if (d->m_scale <= 0 || d->m_scale == 1.0) {
255  return icon();
256  }
257 
258  // Try to load it
259  d->m_scaledIcon = d->loadIcon(resolvePath(d->m_iconPath), d->scaledSize(d->m_size));
260 
261  if (d->m_scaledIcon.isNull()) {
262  // Direct loading failed, try to scale the icon as a last resort
263  QImage const image = icon();
264  if (!image.isNull()) {
265  QSize iconSize = d->scaledSize(image.size());
266  d->m_scaledIcon = image.scaled( iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation ) ;
267  }
268  }
269  return d->m_scaledIcon;
270 }
271 
272 int GeoDataIconStyle::heading() const
273 {
274  return d->m_heading;
275 }
276 
277 void GeoDataIconStyle::setHeading( int heading )
278 {
279  d->m_heading = heading;
280 }
281 
282 RemoteIconLoader *GeoDataIconStyle::remoteIconLoader() const
283 {
284  return d->remoteIconLoader();
285 }
286 
287 void GeoDataIconStyle::pack( QDataStream& stream ) const
288 {
289  GeoDataColorStyle::pack( stream );
290 
291  stream << d->m_scale;
292  stream << d->m_icon;
293  d->m_hotSpot.pack( stream );
294 }
295 
296 void GeoDataIconStyle::unpack( QDataStream& stream )
297 {
298  GeoDataColorStyle::unpack( stream );
299 
300  stream >> d->m_scale;
301  stream >> d->m_icon;
302  d->m_hotSpot.unpack( stream );
303 }
304 
305 }
void pack(QDataStream &stream) const override
Serialize the style to a stream.
void setFileName(const QString &fileName)
void setScaledSize(const QSize &size)
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
int width() const const
QSize size() const const
bool load(QIODevice *device, const char *format)
bool operator==(const Qt3DRender::QGraphicsApiFilter &reference, const Qt3DRender::QGraphicsApiFilter &sample)
KeepAspectRatio
int height() const const
bool isEmpty() const const
bool isNull() const const
QImage read()
bool isNull() const const
Binds a QML item to a specific geodetic location in screen coordinates.
QSize size() const const
GeoDataColorStyle & operator=(const GeoDataColorStyle &other)
assignment operator
QString path(const QString &relativePath)
bool isValid(QStringView ifopt)
QSize scaled(int width, int height, Qt::AspectRatioMode mode) const const
void scale(int width, int height, Qt::AspectRatioMode mode)
SmoothTransformation
void unpack(QDataStream &stream) override
Unserialize the style from a stream.
QDebug mDebug()
a function to replace qDebug() in Marble library code
Definition: MarbleDebug.cpp:31
QString errorString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Sep 25 2023 03:50:18 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.