File indexing completed on 2024-05-12 03:50:12

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2007 Murad Tagirov <tmurad@gmail.com>
0004 //
0005 
0006 
0007 #include "GeoDataIconStyle.h"
0008 
0009 #include <QUrl>
0010 
0011 #include "MarbleDirs.h"
0012 #include "MarbleDebug.h"
0013 #include "RemoteIconLoader.h"
0014 
0015 #include "GeoDataTypes.h"
0016 
0017 #include <QImageReader>
0018 
0019 namespace Marble
0020 {
0021 
0022 class GeoDataIconStylePrivate
0023 {
0024   public:
0025     GeoDataIconStylePrivate()
0026         : m_scale( 1.0 ),
0027         m_size(0, 0),
0028         m_aspectRatioMode(Qt::KeepAspectRatio),
0029         m_iconPath(),
0030         m_heading( 0 )
0031     {
0032     }
0033 
0034     GeoDataIconStylePrivate( const QString& iconPath, const QPointF &hotSpot )
0035         : m_scale( 1.0 ),
0036           m_size(0, 0),
0037           m_aspectRatioMode(Qt::KeepAspectRatio),
0038           m_iconPath( iconPath ),
0039           m_hotSpot( hotSpot ),
0040           m_heading( 0 )
0041     {
0042     }
0043 
0044     RemoteIconLoader* remoteIconLoader() const
0045     {
0046         static RemoteIconLoader *remoteIconLoader = new RemoteIconLoader();
0047         return remoteIconLoader;
0048     }
0049 
0050     QSize scaledSize(const QSize &size) const
0051     {
0052         QSize iconSize = size.isNull() ? m_icon.size() : size;
0053         // Scaling the placemark's icon based on its size, scale, and maximum icon size.
0054         if ( iconSize.width()*m_scale > s_maximumIconSize.width()
0055              || iconSize.height()*m_scale > s_maximumIconSize.height() ) {
0056             iconSize.scale( s_maximumIconSize, Qt::KeepAspectRatio );
0057         }
0058         else if ( iconSize.width()*m_scale < s_minimumIconSize.width()
0059                   || iconSize.height()*m_scale < s_minimumIconSize.width() ) {
0060             iconSize.scale( s_minimumIconSize, Qt::KeepAspectRatio );
0061         }
0062         else {
0063             iconSize *= m_scale;
0064         }
0065 
0066         return QSize(iconSize.width() - iconSize.width() % 2,
0067                      iconSize.height() - iconSize.height() % 2);
0068     }
0069 
0070     QImage loadIcon(const QString &path, const QSize &size) const
0071     {
0072         if (!path.isEmpty()) {
0073             // Icons from the local file system
0074             if (!size.isNull()) {
0075                 QImageReader imageReader;
0076                 imageReader.setFileName(path);
0077                 auto const imageSize = imageReader.size();
0078                 auto const finalSize = imageSize.scaled(size, m_aspectRatioMode);
0079                 imageReader.setScaledSize(finalSize);
0080                 QImage icon = imageReader.read();
0081                 if (icon.isNull()) {
0082                     mDebug() << "GeoDataIconStyle: Failed to read image " << path << ": " << imageReader.errorString();
0083                 }
0084                 return icon;
0085             }
0086             QImage icon = QImage(path);
0087             if (!icon.isNull()) {
0088                 return icon;
0089             }
0090         }
0091 
0092         if(QUrl(m_iconPath).isValid() ) {
0093             // if image is not found on disk, check whether the icon is
0094             // at remote location. If yes then go for remote icon loading
0095             return remoteIconLoader()->load(QUrl(m_iconPath));
0096         }
0097 
0098         mDebug() << "Unable to open style icon at: " << path;
0099         return QImage();
0100     }
0101 
0102     float            m_scale;
0103 
0104     QImage           m_icon;
0105     QSize            m_size;
0106     Qt::AspectRatioMode m_aspectRatioMode;
0107     QImage           m_scaledIcon;
0108     QString          m_iconPath;
0109     GeoDataHotSpot   m_hotSpot;
0110     int              m_heading;
0111 };
0112 
0113 GeoDataIconStyle::GeoDataIconStyle() :
0114     d( new GeoDataIconStylePrivate() )
0115 {
0116 }
0117 
0118 GeoDataIconStyle::GeoDataIconStyle( const GeoDataIconStyle& other ) :
0119     GeoDataColorStyle( other ), d( new GeoDataIconStylePrivate( *other.d ) )
0120 {
0121 }
0122 
0123 GeoDataIconStyle::GeoDataIconStyle( const QString& iconPath, const QPointF &hotSpot ) :
0124     d( new GeoDataIconStylePrivate( iconPath, hotSpot ) )
0125 {
0126 }
0127 
0128 GeoDataIconStyle::~GeoDataIconStyle()
0129 {
0130     delete d;
0131 }
0132 
0133 GeoDataIconStyle& GeoDataIconStyle::operator=( const GeoDataIconStyle& other )
0134 {
0135     GeoDataColorStyle::operator=( other );
0136     *d = *other.d;
0137     return *this;
0138 }
0139 
0140 bool GeoDataIconStyle::operator==( const GeoDataIconStyle &other ) const
0141 {
0142     if ( GeoDataColorStyle::operator!=( other ) ) {
0143         return false;
0144     }
0145 
0146     return d->m_scale == other.d->m_scale &&
0147            d->m_icon == other.d->m_icon &&
0148            d->m_size == other.d->m_size &&
0149            d->m_iconPath == other.d->m_iconPath &&
0150            d->m_hotSpot == other.d->m_hotSpot &&
0151            d->m_heading == other.d->m_heading;
0152 }
0153 
0154 bool GeoDataIconStyle::operator!=( const GeoDataIconStyle &other ) const
0155 {
0156     return !this->operator==( other );
0157 }
0158 
0159 const char* GeoDataIconStyle::nodeType() const
0160 {
0161     return GeoDataTypes::GeoDataIconStyleType;
0162 }
0163 
0164 void GeoDataIconStyle::setIcon(const QImage &icon)
0165 {
0166     d->m_icon = icon;
0167     d->m_scaledIcon = QImage();
0168 }
0169 
0170 void GeoDataIconStyle::setIconPath( const QString& filename )
0171 {
0172     d->m_iconPath = filename;
0173 
0174     /**
0175      * Set the m_icon to be a default-constructed icon
0176      * so that m_icon is null and icon() doesn't return
0177      * previously loaded icon.
0178      */
0179     d->m_icon = QImage();
0180     d->m_scaledIcon = QImage();
0181 }
0182 
0183 QString GeoDataIconStyle::iconPath() const
0184 {
0185     return d->m_iconPath;
0186 }
0187 
0188 QImage GeoDataIconStyle::icon() const
0189 {
0190     if ( !d->m_icon.isNull() ) {
0191         return d->m_icon;
0192     }
0193     else if ( !d->m_iconPath.isEmpty() ) {
0194         d->m_icon = d->loadIcon(resolvePath(d->m_iconPath), d->m_size);
0195         return d->m_icon;
0196     }
0197     else
0198         return QImage();
0199 }
0200 
0201 void GeoDataIconStyle::setHotSpot( const QPointF& hotSpot,
0202                                    GeoDataHotSpot::Units xunits,
0203                                    GeoDataHotSpot::Units yunits )
0204 {
0205     d->m_hotSpot.setHotSpot( hotSpot, xunits, yunits );
0206 }
0207 
0208 QPointF GeoDataIconStyle::hotSpot( GeoDataHotSpot::Units &xunits, GeoDataHotSpot::Units &yunits ) const
0209 {
0210     return d->m_hotSpot.hotSpot( xunits, yunits );
0211 }
0212 
0213 void GeoDataIconStyle::setSize(const QSize &size, Qt::AspectRatioMode aspectRatioMode)
0214 {
0215     if (size == d->m_size && aspectRatioMode == d->m_aspectRatioMode) {
0216         return;
0217     }
0218 
0219     d->m_aspectRatioMode = aspectRatioMode;
0220     d->m_size = QSize(size.width() - size.width() % 2, size.height() - size.height() % 2);
0221     if (!d->m_size.isNull() && !d->m_icon.isNull()) {
0222         // Resize existing icon that cannot be restored from an image path
0223         d->m_icon = d->m_icon.scaled(d->m_size);
0224     } else if (!d->m_iconPath.isEmpty()) {
0225         // Lazily reload the icons
0226         d->m_icon = QImage();
0227         d->m_scaledIcon = QImage();
0228     }
0229 }
0230 
0231 QSize GeoDataIconStyle::size() const
0232 {
0233     return d->m_size;
0234 }
0235 
0236 void GeoDataIconStyle::setScale(float scale)
0237 {
0238     d->m_scale = scale;
0239     d->m_scaledIcon = QImage();
0240 }
0241 
0242 float GeoDataIconStyle::scale() const
0243 {
0244     return d->m_scale;
0245 }
0246 
0247 QImage GeoDataIconStyle::scaledIcon() const
0248 {
0249     if (!d->m_scaledIcon.isNull()) {
0250         return d->m_scaledIcon;
0251     }
0252 
0253     // Invalid or trivial scale
0254     if (d->m_scale <= 0 || d->m_scale == 1.0) {
0255         return icon();
0256     }
0257 
0258     // Try to load it
0259     d->m_scaledIcon = d->loadIcon(resolvePath(d->m_iconPath), d->scaledSize(d->m_size));
0260 
0261     if (d->m_scaledIcon.isNull()) {
0262         // Direct loading failed, try to scale the icon as a last resort
0263         QImage const image = icon();
0264         if (!image.isNull()) {
0265             QSize iconSize = d->scaledSize(image.size());
0266             d->m_scaledIcon = image.scaled( iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation ) ;
0267         }
0268     }
0269     return d->m_scaledIcon;
0270 }
0271 
0272 int GeoDataIconStyle::heading() const
0273 {
0274     return d->m_heading;
0275 }
0276 
0277 void GeoDataIconStyle::setHeading( int heading )
0278 {
0279     d->m_heading = heading;
0280 }
0281 
0282 RemoteIconLoader *GeoDataIconStyle::remoteIconLoader() const
0283 {
0284     return d->remoteIconLoader();
0285 }
0286 
0287 void GeoDataIconStyle::pack( QDataStream& stream ) const
0288 {
0289     GeoDataColorStyle::pack( stream );
0290 
0291     stream << d->m_scale;
0292     stream << d->m_icon;
0293     d->m_hotSpot.pack( stream );
0294 }
0295 
0296 void GeoDataIconStyle::unpack( QDataStream& stream )
0297 {
0298     GeoDataColorStyle::unpack( stream );
0299 
0300     stream >> d->m_scale;
0301     stream >> d->m_icon;
0302     d->m_hotSpot.unpack( stream );
0303 }
0304 
0305 }