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"