File indexing completed on 2025-01-19 03:57:44
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2010-09-18 0007 * Description : A tool to calibrate the tiling levels 0008 * used in geolocation interface 0009 * 0010 * SPDX-FileCopyrightText: 2010 by Michael G. Hansen <mike at mghansen dot de> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "calibrator.h" 0017 0018 // Qt includes 0019 0020 #include <QAction> 0021 #include <QButtonGroup> 0022 #include <QHBoxLayout> 0023 #include <QLabel> 0024 #include <QPushButton> 0025 #include <QRadioButton> 0026 #include <QSpinBox> 0027 #include <QStandardItemModel> 0028 #include <QTimer> 0029 #include <QToolButton> 0030 #include <QVBoxLayout> 0031 #include <QApplication> 0032 #include <QCommandLineParser> 0033 #include <QLineEdit> 0034 0035 // Local includes 0036 0037 #include "digikam_debug.h" 0038 #include "abstractmarkertiler.h" 0039 #include "itemmarkertiler.h" 0040 #include "mapwidget.h" 0041 #include "geocoordinates.h" 0042 #include "digikam_version.h" 0043 0044 using namespace Digikam; 0045 0046 const int CoordinatesRole = Qt::UserRole + 1; 0047 0048 class Q_DECL_HIDDEN CalibratorModelHelper::Private 0049 { 0050 public: 0051 0052 explicit Private() 0053 : model(nullptr) 0054 { 0055 } 0056 0057 QStandardItemModel* model; 0058 }; 0059 0060 CalibratorModelHelper::CalibratorModelHelper(QStandardItemModel* const model, 0061 QObject* const parent) 0062 : GeoModelHelper(parent), 0063 d (new Private()) 0064 { 0065 d->model = model; 0066 } 0067 0068 CalibratorModelHelper::~CalibratorModelHelper() 0069 { 0070 delete d; 0071 } 0072 0073 QAbstractItemModel* CalibratorModelHelper::model() const 0074 { 0075 return d->model; 0076 } 0077 0078 QItemSelectionModel* CalibratorModelHelper::selectionModel() const 0079 { 0080 return nullptr; 0081 } 0082 0083 bool CalibratorModelHelper::itemCoordinates(const QModelIndex& index, 0084 GeoCoordinates* const coordinates) const 0085 { 0086 if (!index.isValid()) 0087 return false; 0088 0089 const QVariant coordinatesVariant = index.data(CoordinatesRole); 0090 GeoCoordinates itemCoordinates = coordinatesVariant.value<GeoCoordinates>(); 0091 0092 if (coordinates) 0093 *coordinates = itemCoordinates; 0094 0095 return itemCoordinates.hasCoordinates(); 0096 } 0097 0098 void CalibratorModelHelper::setItemCoordinates(const QModelIndex& index, 0099 const GeoCoordinates& coordinates) 0100 { 0101 if (!index.isValid()) 0102 return; 0103 0104 d->model->setData(index, QVariant::fromValue(coordinates), CoordinatesRole); 0105 } 0106 0107 GeoModelHelper::PropertyFlags CalibratorModelHelper::modelFlags() const 0108 { 0109 return FlagVisible; 0110 } 0111 0112 // --------------------------------------------------------------------------------------------- 0113 0114 class Q_DECL_HIDDEN Calibrator::Private 0115 { 0116 public: 0117 0118 explicit Private() 0119 : hBoxLayout (nullptr), 0120 model (nullptr), 0121 modelHelper (nullptr), 0122 markerTiler (nullptr), 0123 groupingMode (nullptr), 0124 sbLevel (nullptr), 0125 zoomDisplay (nullptr), 0126 zoomDisplayTimer (nullptr) 0127 { 0128 } 0129 0130 QHBoxLayout* hBoxLayout; 0131 QList<QPair<QWidget*, MapWidget*> > extraWidgetHolders; 0132 QStandardItemModel* model; 0133 CalibratorModelHelper* modelHelper; 0134 ItemMarkerTiler* markerTiler; 0135 0136 QButtonGroup* groupingMode; 0137 QSpinBox* sbLevel; 0138 QLineEdit* zoomDisplay; 0139 QTimer* zoomDisplayTimer; 0140 }; 0141 0142 Calibrator::Calibrator(QWidget* const parent) 0143 : QMainWindow(parent), 0144 d (new Private()) 0145 { 0146 d->model = new QStandardItemModel(this); 0147 d->modelHelper = new CalibratorModelHelper(d->model, this); 0148 d->markerTiler = new ItemMarkerTiler(d->modelHelper, this); 0149 0150 QVBoxLayout* const vboxLayout1 = new QVBoxLayout(); 0151 QWidget* const dummy1 = new QWidget(this); 0152 dummy1->setLayout(vboxLayout1); 0153 setCentralWidget(dummy1); 0154 0155 d->hBoxLayout = new QHBoxLayout(); 0156 vboxLayout1->addLayout(d->hBoxLayout); 0157 0158 d->groupingMode = new QButtonGroup(this); 0159 d->groupingMode->setExclusive(true); 0160 QRadioButton* const buttonGrouped = new QRadioButton(QLatin1String("Grouped"), this); 0161 d->groupingMode->addButton(buttonGrouped, 0); 0162 QRadioButton* const buttonUngrouped = new QRadioButton(QLatin1String("Ungrouped"), this); 0163 d->groupingMode->addButton(buttonUngrouped, 1); 0164 buttonGrouped->setChecked(true); 0165 0166 d->sbLevel = new QSpinBox(this); 0167 d->sbLevel->setMinimum(1); 0168 d->sbLevel->setMaximum(TileIndex::MaxLevel); 0169 QLabel* const labelsbLevel = new QLabel(QLatin1String("Level:"), this); 0170 labelsbLevel->setBuddy(d->sbLevel); 0171 0172 d->zoomDisplay = new QLineEdit(this); 0173 d->zoomDisplay->setReadOnly(true); 0174 QLabel* const labelZoomDisplay = new QLabel(QLatin1String("Zoom:"), this); 0175 labelZoomDisplay->setBuddy(d->zoomDisplay); 0176 0177 QHBoxLayout* const hboxLayout1 = new QHBoxLayout(this); 0178 hboxLayout1->addWidget(new QLabel(QLatin1String("Display mode:"), this)); 0179 hboxLayout1->addWidget(buttonGrouped); 0180 hboxLayout1->addWidget(buttonUngrouped); 0181 hboxLayout1->addWidget(labelsbLevel); 0182 hboxLayout1->addWidget(d->sbLevel); 0183 hboxLayout1->addWidget(labelZoomDisplay); 0184 hboxLayout1->addWidget(d->zoomDisplay); 0185 hboxLayout1->addStretch(10); 0186 vboxLayout1->addLayout(hboxLayout1); 0187 0188 QHBoxLayout* const hboxLayout2 = new QHBoxLayout(this); 0189 QPushButton* const pbAddMap = new QPushButton(QLatin1String("Add Map Widget"), this); 0190 hboxLayout2->addWidget(pbAddMap); 0191 QPushButton* const pbRemoveMap = new QPushButton(QLatin1String("Remove Map Widget"), this); 0192 hboxLayout2->addWidget(pbRemoveMap); 0193 vboxLayout1->addLayout(hboxLayout2); 0194 0195 #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) 0196 0197 connect(d->groupingMode, SIGNAL(idClicked(int)), 0198 this, SLOT(updateGroupingMode())); 0199 0200 #else 0201 0202 connect(d->groupingMode, SIGNAL(buttonClicked(int)), 0203 this, SLOT(updateGroupingMode())); 0204 0205 #endif 0206 0207 connect(d->sbLevel, SIGNAL(valueChanged(int)), 0208 this, SLOT(updateMarkers())); 0209 0210 connect(pbAddMap, SIGNAL(clicked()), 0211 this, SLOT(slotAddMapWidget())); 0212 0213 connect(pbRemoveMap, SIGNAL(clicked()), 0214 this, SLOT(slotRemoveMapWidget())); 0215 0216 updateMarkers(); 0217 updateGroupingMode(); 0218 0219 slotAddMapWidget(); 0220 0221 d->zoomDisplayTimer = new QTimer(this); 0222 d->zoomDisplayTimer->start(200); 0223 0224 connect(d->zoomDisplayTimer, SIGNAL(timeout()), 0225 this, SLOT(updateZoomView())); 0226 } 0227 0228 Calibrator::~Calibrator() 0229 { 0230 delete d; 0231 } 0232 0233 void Calibrator::updateGroupingMode() 0234 { 0235 const bool shouldBeGrouped = (d->groupingMode->checkedId() == 0); 0236 0237 for (int i = 0 ; i < d->extraWidgetHolders.count() ; ++i) 0238 { 0239 MapWidget* const mapWidget = d->extraWidgetHolders.at(i).second; 0240 0241 if (shouldBeGrouped) 0242 { 0243 mapWidget->removeUngroupedModel(d->modelHelper); 0244 mapWidget->setGroupedModel(d->markerTiler); 0245 } 0246 else 0247 { 0248 mapWidget->setGroupedModel(nullptr); 0249 mapWidget->addUngroupedModel(d->modelHelper); 0250 } 0251 } 0252 } 0253 0254 void Calibrator::addMarkerAt(const GeoCoordinates& coordinates) 0255 { 0256 qCDebug(DIGIKAM_TESTS_LOG) << coordinates; 0257 QStandardItem* const item = new QStandardItem(coordinates.geoUrl()); 0258 item->setData(QVariant::fromValue(coordinates), CoordinatesRole); 0259 0260 d->model->appendRow(item); 0261 } 0262 0263 void Calibrator::updateMarkers() 0264 { 0265 d->model->clear(); 0266 0267 const int newLevel = d->sbLevel->value(); 0268 const int Tiling = TileIndex::Tiling; 0269 0270 // add markers in all four corners and in the middle of the edges: 0271 typedef QPair<int, int> QIntPair; 0272 QList<QIntPair> partialTilePositions; 0273 0274 // corners: 0275 partialTilePositions 0276 << QIntPair(0, 0) 0277 << QIntPair(Tiling-1, Tiling-1) 0278 << QIntPair(Tiling*(Tiling-1), Tiling*(Tiling-1)) 0279 << QIntPair(Tiling*Tiling-1, Tiling*Tiling-1); 0280 0281 // middle of edges: 0282 partialTilePositions 0283 << QIntPair(Tiling/2, 0) 0284 << QIntPair(Tiling*(Tiling/2), 0) 0285 << QIntPair(Tiling*(Tiling/2)+Tiling-1, (Tiling-1)) 0286 << QIntPair(Tiling*Tiling-Tiling/2-1, Tiling*Tiling-1); 0287 0288 // center of the map: 0289 partialTilePositions 0290 << QIntPair(Tiling*(Tiling/2)+Tiling/2, 0) 0291 << QIntPair(Tiling*(Tiling/2-1)+Tiling/2, Tiling*(Tiling-1)) 0292 << QIntPair(Tiling*(Tiling/2-1)+Tiling/2-1, Tiling*Tiling-1) 0293 << QIntPair(Tiling*(Tiling/2)+Tiling/2-1, Tiling-1); 0294 0295 // at +/- ~70 degrees (cutoff of Mercator projection is at 80): 0296 partialTilePositions 0297 << QIntPair(Tiling, 0) 0298 << QIntPair(2*Tiling-1, Tiling-1) 0299 << QIntPair(Tiling*(Tiling-2), Tiling*(Tiling-1)) 0300 << QIntPair(Tiling*(Tiling-1)-1, Tiling*Tiling-1); 0301 0302 for (int ptp = 0; ptp<partialTilePositions.count(); ++ptp) 0303 { 0304 QIntPair currentPair = partialTilePositions.at(ptp); 0305 const int level0Index = currentPair.first; 0306 const int followingIndex = currentPair.second; 0307 0308 TileIndex markerIndex; 0309 markerIndex.appendLinearIndex(level0Index); 0310 0311 for (int level = 1 ; level < newLevel-2 ; level++) 0312 { 0313 markerIndex.appendLinearIndex(followingIndex); 0314 } 0315 0316 const int smallPart = followingIndex % Tiling; 0317 0318 for (int i = -1 ; i <= 1 ; ++i) 0319 { 0320 if ((smallPart+i >= 0) && (smallPart+i < Tiling)) 0321 { 0322 for (int j = -1 ; j <= 1 ; ++j) 0323 { 0324 const int newLinIndex = followingIndex + i + j*Tiling; 0325 0326 if ((newLinIndex >= 0) && (newLinIndex < Tiling*Tiling)) 0327 { 0328 TileIndex newIndex = markerIndex; 0329 newIndex.appendLinearIndex(newLinIndex); 0330 addMarkerAt(newIndex.toCoordinates()); 0331 /* 0332 for (int corner = 1 ; corner <= 4 ; ++corner) 0333 { 0334 addMarkerAt(newIndex.toCoordinates(TileIndex::CornerPosition(corner))); 0335 } 0336 */ 0337 } 0338 } 0339 } 0340 } 0341 } 0342 0343 qCDebug(DIGIKAM_TESTS_LOG)<<d->model->rowCount(); 0344 } 0345 0346 void Calibrator::updateZoomView() 0347 { 0348 if (d->extraWidgetHolders.isEmpty()) 0349 { 0350 return; 0351 } 0352 0353 MapWidget* const firstMapWidget = d->extraWidgetHolders.first().second; 0354 const QString newZoom = firstMapWidget->getZoom(); 0355 0356 if (newZoom!=d->zoomDisplay->text()) 0357 { 0358 d->zoomDisplay->setText(newZoom); 0359 } 0360 } 0361 0362 void Calibrator::slotAddMapWidget() 0363 { 0364 QVBoxLayout* const boxLayout = new QVBoxLayout(); 0365 MapWidget* const mapWidget = new MapWidget(); 0366 boxLayout->addWidget(mapWidget); 0367 boxLayout->addWidget(mapWidget->getControlWidget()); 0368 0369 QAction* const activateMapAction = new QAction(QLatin1String("Active"), mapWidget); 0370 activateMapAction->setData(QVariant::fromValue<void*>(mapWidget)); 0371 activateMapAction->setCheckable(true); 0372 QToolButton* const toolButton = new QToolButton(mapWidget); 0373 toolButton->setDefaultAction(activateMapAction); 0374 mapWidget->addWidgetToControlWidget(toolButton); 0375 0376 connect(activateMapAction, SIGNAL(triggered(bool)), 0377 this, SLOT(slotActivateMapActionTriggered(bool))); 0378 0379 QWidget* const dummyWidget = new QWidget(); 0380 dummyWidget->setLayout(boxLayout); 0381 d->extraWidgetHolders.append(QPair<QWidget*, MapWidget*>(dummyWidget, mapWidget)); 0382 0383 d->hBoxLayout->addWidget(dummyWidget); 0384 0385 updateGroupingMode(); 0386 } 0387 0388 void Calibrator::slotRemoveMapWidget() 0389 { 0390 if (d->extraWidgetHolders.isEmpty()) 0391 { 0392 return; 0393 } 0394 0395 QPair<QWidget*, MapWidget*> info = d->extraWidgetHolders.takeLast(); 0396 d->hBoxLayout->removeWidget(info.first); 0397 delete info.first; 0398 } 0399 0400 void Calibrator::slotActivateMapActionTriggered(bool state) 0401 { 0402 QAction* const senderAction = qobject_cast<QAction*>(sender()); 0403 0404 if (!senderAction) 0405 { 0406 return; 0407 } 0408 0409 MapWidget* const mapWidget = static_cast<MapWidget*>(senderAction->data().value<void*>()); 0410 mapWidget->setActive(state); 0411 } 0412 0413 int main(int argc, char* argv[]) 0414 { 0415 QApplication app(argc, argv); 0416 QCommandLineParser parser; 0417 parser.addVersionOption(); 0418 parser.addHelpOption(); 0419 parser.process(app); 0420 0421 Calibrator* const calibrator = new Calibrator(); 0422 calibrator->show(); 0423 0424 return app.exec(); 0425 } 0426 0427 #include "moc_calibrator.cpp"