File indexing completed on 2025-01-05 03:56:42
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2006-02-22 0007 * Description : a tab widget to display GPS info 0008 * 0009 * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2011 by Michael G. Hansen <mike at mghansen dot de> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 /** 0017 * NOTE: Good explanations about GPS (in French) can be found at this url : 0018 * www.gpspassion.com/forumsen/topic.asp?TOPIC_ID=16593 0019 */ 0020 0021 #include "itempropertiesgpstab.h" 0022 0023 // Qt includes 0024 0025 #include <QFile> 0026 #include <QFrame> 0027 #include <QGridLayout> 0028 #include <QGroupBox> 0029 #include <QLabel> 0030 #include <QMap> 0031 #include <QPushButton> 0032 #include <QToolButton> 0033 #include <QStandardItemModel> 0034 #include <QStandardItem> 0035 #include <QComboBox> 0036 #include <QDesktopServices> 0037 #include <QIcon> 0038 #include <QLocale> 0039 #include <QScopedPointer> 0040 #include <QStackedWidget> 0041 0042 // KDE includes 0043 0044 #include <kconfiggroup.h> 0045 #include <klocalizedstring.h> 0046 0047 // Local includes 0048 0049 #include "mapwidget.h" 0050 #include "itemmarkertiler.h" 0051 #include "dlayoutbox.h" 0052 #include "digikam_debug.h" 0053 #include "itemgpsmodelhelper.h" 0054 #include "dexpanderbox.h" 0055 #include "digikam_config.h" 0056 0057 #ifdef HAVE_QWEBENGINE 0058 # include "webbrowserdlg.h" 0059 #endif 0060 0061 namespace Digikam 0062 { 0063 0064 class Q_DECL_HIDDEN ItemPropertiesGPSTab::Private 0065 { 0066 0067 public: 0068 0069 explicit Private() 0070 : altLabel (nullptr), 0071 latLabel (nullptr), 0072 lonLabel (nullptr), 0073 dateLabel (nullptr), 0074 mapView (nullptr), 0075 detailsBtn (nullptr), 0076 detailsCombo (nullptr), 0077 altitude (nullptr), 0078 latitude (nullptr), 0079 longitude (nullptr), 0080 date (nullptr), 0081 map (nullptr), 0082 itemMarkerTiler (nullptr), 0083 itemModel (nullptr), 0084 gpsModelHelper (nullptr), 0085 gpsItemInfoSorter (nullptr), 0086 boundariesShouldBeAdjusted (false) 0087 { 0088 } 0089 0090 QLabel* altLabel; 0091 QLabel* latLabel; 0092 QLabel* lonLabel; 0093 QLabel* dateLabel; 0094 0095 QStackedWidget* mapView; 0096 QToolButton* detailsBtn; 0097 QComboBox* detailsCombo; 0098 0099 DAdjustableLabel* altitude; 0100 DAdjustableLabel* latitude; 0101 DAdjustableLabel* longitude; 0102 DAdjustableLabel* date; 0103 0104 MapWidget* map; 0105 ItemMarkerTiler* itemMarkerTiler; 0106 GPSItemInfo::List gpsInfoList; 0107 0108 QStandardItemModel* itemModel; 0109 ItemGPSModelHelper* gpsModelHelper; 0110 GPSItemInfoSorter* gpsItemInfoSorter; 0111 bool boundariesShouldBeAdjusted; 0112 }; 0113 0114 ItemPropertiesGPSTab::ItemPropertiesGPSTab(QWidget* const parent) 0115 : QWidget(parent), 0116 d (new Private) 0117 { 0118 QGridLayout* const layout = new QGridLayout(this); 0119 0120 // -------------------------------------------------------- 0121 0122 d->mapView = new QStackedWidget(this); 0123 d->mapView->setMinimumWidth(100); 0124 d->mapView->setMinimumHeight(100); 0125 d->mapView->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); 0126 d->mapView->setLineWidth(style()->pixelMetric(QStyle::PM_DefaultFrameWidth)); 0127 0128 QWidget* const mapPanel = new QWidget(this); 0129 QVBoxLayout* const vlay2 = new QVBoxLayout(mapPanel); 0130 d->map = new MapWidget(mapPanel); 0131 d->map->setAvailableMouseModes(MouseModePan | MouseModeZoomIntoGroup); 0132 d->map->setVisibleMouseModes(MouseModePan | MouseModeZoomIntoGroup); 0133 d->map->setEnabledExtraActions(ExtraActionSticky); 0134 d->map->setVisibleExtraActions(ExtraActionSticky); 0135 d->map->setBackend(QLatin1String("marble")); 0136 d->gpsItemInfoSorter = new GPSItemInfoSorter(this); 0137 d->gpsItemInfoSorter->addToMapWidget(d->map); 0138 vlay2->addWidget(d->map); 0139 vlay2->setContentsMargins(QMargins()); 0140 vlay2->setSpacing(0); 0141 0142 d->mapView->insertWidget(0, mapPanel); 0143 0144 QLabel* const noGPSInfo = new QLabel(i18n("No Geolocation\nInformation\nAvailable")); 0145 noGPSInfo->setAlignment(Qt::AlignCenter); 0146 noGPSInfo->setWordWrap(true); 0147 d->mapView->insertWidget(1, noGPSInfo); 0148 0149 // -------------------------------------------------------- 0150 0151 d->itemModel = new QStandardItemModel(this); 0152 d->gpsModelHelper = new ItemGPSModelHelper(d->itemModel, this); 0153 d->itemMarkerTiler = new ItemMarkerTiler(d->gpsModelHelper, this); 0154 d->map->setGroupedModel(d->itemMarkerTiler); 0155 0156 d->altLabel = new QLabel(i18n("<b>Altitude</b>:"), this); 0157 d->latLabel = new QLabel(i18n("<b>Latitude</b>:"), this); 0158 d->lonLabel = new QLabel(i18n("<b>Longitude</b>:"), this); 0159 d->dateLabel = new QLabel(i18n("<b>Date</b>:"), this); 0160 d->altitude = new DAdjustableLabel(this); 0161 d->latitude = new DAdjustableLabel(this); 0162 d->longitude = new DAdjustableLabel(this); 0163 d->date = new DAdjustableLabel(this); 0164 d->altitude->setAlignment(Qt::AlignRight); 0165 d->latitude->setAlignment(Qt::AlignRight); 0166 d->longitude->setAlignment(Qt::AlignRight); 0167 d->date->setAlignment(Qt::AlignRight); 0168 0169 // -------------------------------------------------------- 0170 0171 QWidget* const box = new DHBox(this); 0172 QHBoxLayout* const hBoxLayout = reinterpret_cast<QHBoxLayout*>(box->layout()); 0173 0174 if (hBoxLayout) 0175 { 0176 hBoxLayout->addStretch(); 0177 } 0178 0179 d->detailsCombo = new QComboBox(box); 0180 d->detailsBtn = new QToolButton(box); 0181 d->detailsBtn->setIcon(QIcon::fromTheme(QLatin1String("globe"))); 0182 d->detailsBtn->setToolTip(i18n("See more information on the Internet")); 0183 d->detailsCombo->insertItem(MapQuest, QLatin1String("MapQuest")); 0184 d->detailsCombo->insertItem(GoogleMaps, QLatin1String("Google Maps")); 0185 d->detailsCombo->insertItem(BingMaps, QLatin1String("Bing Maps")); 0186 d->detailsCombo->insertItem(OpenStreetMap, QLatin1String("OpenStreetMap")); 0187 d->detailsCombo->insertItem(LocAlizeMaps, QLatin1String("loc.alize.us Maps")); 0188 0189 // -------------------------------------------------------- 0190 0191 layout->addWidget(d->mapView, 0, 0, 1, 2); 0192 layout->addWidget(d->altLabel, 1, 0, 1, 1); 0193 layout->addWidget(d->altitude, 1, 1, 1, 1); 0194 layout->addWidget(d->latLabel, 2, 0, 1, 1); 0195 layout->addWidget(d->latitude, 2, 1, 1, 1); 0196 layout->addWidget(d->lonLabel, 3, 0, 1, 1); 0197 layout->addWidget(d->longitude, 3, 1, 1, 1); 0198 layout->addWidget(d->dateLabel, 4, 0, 1, 1); 0199 layout->addWidget(d->date, 4, 1, 1, 1); 0200 layout->addWidget(d->map->getControlWidget(), 5, 0, 1, 2); 0201 layout->addWidget(box, 6, 0, 1, 2); 0202 layout->setRowStretch(0, 10); 0203 layout->setColumnStretch(1, 10); 0204 layout->setContentsMargins(QMargins()); 0205 layout->setSpacing(0); 0206 0207 // -------------------------------------------------------- 0208 0209 connect(d->detailsBtn, SIGNAL(clicked()), 0210 this, SLOT(slotGPSDetails())); 0211 } 0212 0213 ItemPropertiesGPSTab::~ItemPropertiesGPSTab() 0214 { 0215 delete d; 0216 } 0217 0218 void ItemPropertiesGPSTab::readSettings(const KConfigGroup& group) 0219 { 0220 d->gpsItemInfoSorter->setSortOptions(GPSItemInfoSorter::SortOptions(group.readEntry(QLatin1String("Sort Order"), 0221 int(d->gpsItemInfoSorter->getSortOptions())))); 0222 setWebGPSLocator(group.readEntry(QLatin1String("Web GPS Locator"), getWebGPSLocator())); 0223 0224 KConfigGroup groupMapWidget = KConfigGroup(&group, QLatin1String("Map Widget")); 0225 d->map->readSettingsFromGroup(&groupMapWidget); 0226 0227 } 0228 void ItemPropertiesGPSTab::writeSettings(KConfigGroup& group) 0229 { 0230 group.writeEntry(QLatin1String("Sort Order"), int(d->gpsItemInfoSorter->getSortOptions())); 0231 group.writeEntry(QLatin1String("Web GPS Locator"), getWebGPSLocator()); 0232 0233 KConfigGroup groupMapWidget = KConfigGroup(&group, QLatin1String("Map Widget")); 0234 d->map->saveSettingsToGroup(&groupMapWidget); 0235 } 0236 0237 int ItemPropertiesGPSTab::getWebGPSLocator() const 0238 { 0239 return d->detailsCombo->currentIndex(); 0240 } 0241 0242 void ItemPropertiesGPSTab::setWebGPSLocator(int locator) 0243 { 0244 d->detailsCombo->setCurrentIndex(locator); 0245 } 0246 0247 void ItemPropertiesGPSTab::slotGPSDetails() 0248 { 0249 QString val, url; 0250 0251 if (d->gpsInfoList.isEmpty()) 0252 { 0253 return; 0254 } 0255 0256 const GPSItemInfo info = d->gpsInfoList.first(); 0257 0258 switch ( getWebGPSLocator() ) 0259 { 0260 case MapQuest: 0261 { 0262 url.append(QLatin1String("https://www.mapquest.com/maps/map.adp?searchtype=address" 0263 "&formtype=address&latlongtype=decimal")); 0264 url.append(QLatin1String("&latitude=")); 0265 url.append(val.setNum(info.coordinates.lat(), 'g', 12)); 0266 url.append(QLatin1String("&longitude=")); 0267 url.append(val.setNum(info.coordinates.lon(), 'g', 12)); 0268 break; 0269 } 0270 0271 case GoogleMaps: 0272 { 0273 url.append(QLatin1String("https://maps.google.com/?q=")); 0274 url.append(val.setNum(info.coordinates.lat(), 'g', 12)); 0275 url.append(QLatin1String(",")); 0276 url.append(val.setNum(info.coordinates.lon(), 'g', 12)); 0277 url.append(QLatin1String("&spn=0.05,0.05&t=h&om=1")); 0278 break; 0279 } 0280 0281 case LocAlizeMaps: 0282 { 0283 url.append(QLatin1String("https://loc.alize.us/#/geo:")); 0284 url.append(val.setNum(info.coordinates.lat(), 'g', 12)); 0285 url.append(QLatin1String(",")); 0286 url.append(val.setNum(info.coordinates.lon(), 'g', 12)); 0287 url.append(QLatin1String(",15,/")); 0288 break; 0289 } 0290 0291 case BingMaps: 0292 { 0293 url.append(QLatin1String("https://www.bing.com/maps/?v=2&where1=")); 0294 url.append(val.setNum(info.coordinates.lat(), 'g', 12)); 0295 url.append(QLatin1String(",")); 0296 url.append(val.setNum(info.coordinates.lon(), 'g', 12)); 0297 break; 0298 } 0299 0300 case OpenStreetMap: 0301 { 0302 // lat and lon would also work, but wouldn't show a marker 0303 url.append(QLatin1String("https://www.openstreetmap.org/?")); 0304 url.append(QLatin1String("mlat=")); 0305 url.append(val.setNum(info.coordinates.lat(), 'g', 12)); 0306 url.append(QLatin1String("&mlon=")); 0307 url.append(val.setNum(info.coordinates.lon(), 'g', 12)); 0308 url.append(QLatin1String("&zoom=15")); 0309 break; 0310 } 0311 } 0312 0313 qCDebug(DIGIKAM_GENERAL_LOG) << url; 0314 0315 #ifdef HAVE_QWEBENGINE 0316 0317 WebBrowserDlg* const browser = new WebBrowserDlg(QUrl(url), this); 0318 browser->show(); 0319 0320 #else 0321 0322 QDesktopServices::openUrl(QUrl(url)); 0323 0324 #endif 0325 0326 } 0327 0328 void ItemPropertiesGPSTab::setCurrentURL(const QUrl& url) 0329 { 0330 if (url.isEmpty()) 0331 { 0332 clearGPSInfo(); 0333 return; 0334 } 0335 0336 QScopedPointer<DMetadata> meta(new DMetadata(url.toLocalFile())); 0337 0338 setMetadata(meta.data(), url); 0339 } 0340 0341 void ItemPropertiesGPSTab::setMetadata(DMetadata* const meta, const QUrl& url) 0342 { 0343 double lat, lng; 0344 const bool haveCoordinates = meta->getGPSLatitudeNumber(&lat) && meta->getGPSLongitudeNumber(&lng); 0345 0346 if (haveCoordinates) 0347 { 0348 double alt; 0349 const bool haveAlt = meta->getGPSAltitude(&alt); 0350 0351 GeoCoordinates coordinates(lat, lng); 0352 0353 if (haveAlt) 0354 { 0355 coordinates.setAlt(alt); 0356 } 0357 0358 GPSItemInfo gpsInfo; 0359 gpsInfo.coordinates = coordinates; 0360 gpsInfo.dateTime = meta->getItemDateTime(); 0361 gpsInfo.rating = meta->getItemRating(); 0362 gpsInfo.url = url; 0363 setGPSInfoList(GPSItemInfo::List() << gpsInfo); 0364 } 0365 else 0366 { 0367 clearGPSInfo(); 0368 } 0369 } 0370 0371 void ItemPropertiesGPSTab::clearGPSInfo() 0372 { 0373 d->altitude->setAdjustedText(); 0374 d->latitude->setAdjustedText(); 0375 d->longitude->setAdjustedText(); 0376 d->date->setAdjustedText(); 0377 d->itemModel->clear(); 0378 d->mapView->setCurrentIndex(1); 0379 setEnabled(false); 0380 } 0381 0382 void ItemPropertiesGPSTab::setGPSInfoList(const GPSItemInfo::List& list) 0383 { 0384 // Clear info label 0385 0386 d->altitude->setAdjustedText(); 0387 d->latitude->setAdjustedText(); 0388 d->longitude->setAdjustedText(); 0389 d->date->setAdjustedText(); 0390 d->gpsInfoList.clear(); 0391 d->itemModel->clear(); 0392 d->gpsInfoList = list; 0393 0394 if (list.isEmpty()) 0395 { 0396 clearGPSInfo(); 0397 return; 0398 } 0399 0400 d->mapView->setCurrentIndex(0); 0401 setEnabled(true); 0402 0403 if (list.count() == 1) 0404 { 0405 const GPSItemInfo info = list.first(); 0406 const GeoCoordinates& coordinates = info.coordinates; 0407 0408 if (!coordinates.hasAltitude()) 0409 { 0410 d->altitude->setAdjustedText(i18nc("@label: no GPS coordinates", "Undefined")); 0411 } 0412 else 0413 { 0414 d->altitude->setAdjustedText(QString::fromLatin1("%1 m").arg(QLocale().toString(coordinates.alt(), 'g', 7))); 0415 } 0416 0417 d->latitude->setAdjustedText(QLocale().toString(coordinates.lat(), 'g', 7)); 0418 d->longitude->setAdjustedText(QLocale().toString(coordinates.lon(), 'g', 7)); 0419 d->date->setAdjustedText(QLocale().toString(info.dateTime, QLocale::ShortFormat)); 0420 } 0421 0422 for (int i = 0 ; i < d->gpsInfoList.count() ; ++i) 0423 { 0424 QStandardItem* const currentItemGPSItem = new QStandardItem(); 0425 currentItemGPSItem->setData(QVariant::fromValue(d->gpsInfoList.at(i)), RoleGPSItemInfo); 0426 d->itemModel->appendRow(currentItemGPSItem); 0427 } 0428 0429 if (!d->map->getStickyModeState()) 0430 { 0431 if (!d->map->getActiveState()) 0432 { 0433 d->boundariesShouldBeAdjusted = true; 0434 } 0435 else 0436 { 0437 d->boundariesShouldBeAdjusted = false; 0438 d->map->adjustBoundariesToGroupedMarkers(); 0439 } 0440 } 0441 } 0442 0443 void ItemPropertiesGPSTab::setActive(const bool state) 0444 { 0445 d->map->setActive(state); 0446 0447 if (state && d->boundariesShouldBeAdjusted) 0448 { 0449 d->boundariesShouldBeAdjusted = false; 0450 d->map->adjustBoundariesToGroupedMarkers(); 0451 } 0452 } 0453 0454 } // namespace Digikam 0455 0456 #include "moc_itempropertiesgpstab.cpp"