Marble

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

KDE's Doxygen guidelines are available online.