File indexing completed on 2024-05-05 03:50:49
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2007 Andrew Manson <g.real.ate@gmail.com> 0004 // SPDX-FileCopyrightText: 2009 Eckhart Wörner <ewoerner@kde.org> 0005 // SPDX-FileCopyrightText: 2010 Thibaut Gridel <tgridel@free.fr> 0006 // SPDX-FileCopyrightText: 2010 Daniel Marth <danielmarth@gmx.at> 0007 // 0008 0009 #include "PositionMarker.h" 0010 0011 #include "MarbleDebug.h" 0012 #include <QRect> 0013 #include <qmath.h> 0014 #include <QFileDialog> 0015 #include <QPushButton> 0016 #include <QColorDialog> 0017 #include <QTransform> 0018 0019 #include <cmath> 0020 0021 #include "ui_PositionMarkerConfigWidget.h" 0022 #include "MarbleColors.h" 0023 #include "MarbleModel.h" 0024 #include "MarbleDirs.h" 0025 #include "GeoPainter.h" 0026 #include "PositionTracking.h" 0027 #include "ViewportParams.h" 0028 #include "Planet.h" 0029 #include "GeoDataAccuracy.h" 0030 0031 namespace Marble 0032 { 0033 0034 const int PositionMarker::sm_defaultSizeStep = 2; 0035 const float PositionMarker::sm_resizeSteps[] = { 0.25, 0.5, 1.0, 2.0, 4.0 }; 0036 const int PositionMarker::sm_numResizeSteps = sizeof( sm_resizeSteps ) / sizeof( sm_resizeSteps[0] ); 0037 0038 PositionMarker::PositionMarker( const MarbleModel *marbleModel ) 0039 : RenderPlugin( marbleModel ), 0040 m_marbleModel( marbleModel ), 0041 m_isInitialized( false ), 0042 m_useCustomCursor( false ), 0043 m_defaultCursorPath(MarbleDirs::path(QStringLiteral("svg/track_turtle.svg"))), 0044 m_lastBoundingBox(), 0045 ui_configWidget( nullptr ), 0046 m_configDialog( nullptr ), 0047 m_cursorPath( m_defaultCursorPath ), 0048 m_cursorSize( 1.0 ), 0049 m_accuracyColor( Oxygen::brickRed4 ), 0050 m_trailColor( 0, 0, 255 ), 0051 m_heading( 0.0 ), 0052 m_showTrail ( false ) 0053 { 0054 const bool smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen; 0055 m_accuracyColor.setAlpha( smallScreen ? 80 : 40 ); 0056 } 0057 0058 PositionMarker::~PositionMarker () 0059 { 0060 delete ui_configWidget; 0061 delete m_configDialog; 0062 } 0063 0064 QStringList PositionMarker::renderPosition() const 0065 { 0066 return QStringList(QStringLiteral("HOVERS_ABOVE_SURFACE")); 0067 } 0068 0069 QString PositionMarker::renderPolicy() const 0070 { 0071 return QStringLiteral("ALWAYS"); 0072 } 0073 0074 QStringList PositionMarker::backendTypes() const 0075 { 0076 return QStringList(QStringLiteral("positionmarker")); 0077 } 0078 0079 QString PositionMarker::name() const 0080 { 0081 return tr( "Position Marker" ); 0082 } 0083 0084 QString PositionMarker::guiString() const 0085 { 0086 return tr( "&Position Marker" ); 0087 } 0088 0089 QString PositionMarker::nameId() const 0090 { 0091 return QStringLiteral("positionMarker"); 0092 } 0093 0094 QString PositionMarker::version() const 0095 { 0096 return QStringLiteral("1.0"); 0097 } 0098 0099 QString PositionMarker::description() const 0100 { 0101 return tr( "draws a marker at the current position" ); 0102 } 0103 0104 QString PositionMarker::copyrightYears() const 0105 { 0106 return QStringLiteral("2009, 2010"); 0107 } 0108 0109 QVector<PluginAuthor> PositionMarker::pluginAuthors() const 0110 { 0111 return QVector<PluginAuthor>() 0112 << PluginAuthor(QStringLiteral("Andrew Manson"), QStringLiteral("g.real.ate@gmail.com")) 0113 << PluginAuthor(QStringLiteral("Eckhart Woerner"), QStringLiteral("ewoerner@kde.org")) 0114 << PluginAuthor(QStringLiteral("Thibaut Gridel"), QStringLiteral("tgridel@free.fr")) 0115 << PluginAuthor(QStringLiteral("Daniel Marth"), QStringLiteral("danielmarth@gmx.at")); 0116 } 0117 0118 QIcon PositionMarker::icon() const 0119 { 0120 return QIcon(QStringLiteral(":/icons/positionmarker.png")); 0121 } 0122 0123 QDialog *PositionMarker::configDialog() 0124 { 0125 if ( !m_configDialog ) { 0126 // Initializing configuration dialog 0127 m_configDialog = new QDialog(); 0128 ui_configWidget = new Ui::PositionMarkerConfigWidget; 0129 ui_configWidget->setupUi( m_configDialog ); 0130 ui_configWidget->m_resizeSlider->setMaximum( sm_numResizeSteps - 1 ); 0131 readSettings(); 0132 connect( ui_configWidget->m_buttonBox, SIGNAL(accepted()), 0133 SLOT(writeSettings()) ); 0134 connect( ui_configWidget->m_buttonBox, SIGNAL(rejected()), 0135 SLOT(readSettings()) ); 0136 connect( ui_configWidget->m_buttonBox->button( QDialogButtonBox::RestoreDefaults ), SIGNAL(clicked()), 0137 SLOT(restoreDefaultSettings()) ); 0138 QPushButton *applyButton = ui_configWidget->m_buttonBox->button( QDialogButtonBox::Apply ); 0139 connect( applyButton, SIGNAL(clicked()), 0140 SLOT(writeSettings()) ); 0141 connect( ui_configWidget->m_fileChooserButton, SIGNAL(clicked()), 0142 SLOT(chooseCustomCursor()) ); 0143 connect( ui_configWidget->m_resizeSlider, SIGNAL(valueChanged(int)), 0144 SLOT(resizeCursor(int)) ); 0145 connect( ui_configWidget->m_acColorChooserButton, SIGNAL(clicked()), 0146 SLOT(chooseColor()) ); 0147 connect( ui_configWidget->m_trailColorChooserButton, SIGNAL(clicked()), 0148 SLOT(chooseColor()) ); 0149 } 0150 return m_configDialog; 0151 } 0152 0153 void PositionMarker::initialize() 0154 { 0155 if ( marbleModel() ) { 0156 connect( marbleModel()->positionTracking(), SIGNAL(gpsLocation(GeoDataCoordinates,qreal)), 0157 this, SLOT(setPosition(GeoDataCoordinates)) ); 0158 connect( marbleModel()->positionTracking(), SIGNAL(statusChanged(PositionProviderStatus)), 0159 this, SIGNAL(repaintNeeded()) ); 0160 m_isInitialized = true; 0161 } 0162 loadDefaultCursor(); 0163 } 0164 0165 bool PositionMarker::isInitialized() const 0166 { 0167 return m_isInitialized; 0168 } 0169 0170 bool PositionMarker::render( GeoPainter *painter, 0171 ViewportParams *viewport, 0172 const QString& renderPos, 0173 GeoSceneLayer * layer ) 0174 { 0175 Q_UNUSED( renderPos ) 0176 Q_UNUSED( layer ) 0177 0178 bool const gpsActive = marbleModel()->positionTracking()->positionProviderPlugin() != nullptr; 0179 bool const positionAvailable = marbleModel()->positionTracking()->status() == PositionProviderStatusAvailable; 0180 bool const positionValid = m_currentPosition.isValid(); 0181 if ( gpsActive && positionAvailable && positionValid ) { 0182 m_lastBoundingBox = viewport->viewLatLonAltBox(); 0183 0184 qreal screenPositionX, screenPositionY; 0185 if (!viewport->screenCoordinates( m_currentPosition, screenPositionX, screenPositionY )){ 0186 return true; 0187 } 0188 const GeoDataCoordinates top( m_currentPosition.longitude(), m_currentPosition.latitude()+0.1 ); 0189 qreal screenTopX, screenTopY; 0190 if (!viewport->screenCoordinates( top, screenTopX, screenTopY )){ 0191 return true; 0192 } 0193 qreal const correction = -90.0 + RAD2DEG * atan2( screenPositionY -screenTopY, screenPositionX - screenTopX ); 0194 const qreal rotation = m_heading + correction; 0195 0196 if ( m_useCustomCursor ) { 0197 QTransform transform; 0198 transform.rotate( rotation ); 0199 bool const highQuality = painter->mapQuality() == HighQuality || painter->mapQuality() == PrintQuality; 0200 Qt::TransformationMode const mode = highQuality ? Qt::SmoothTransformation : Qt::FastTransformation; 0201 m_customCursorTransformed = m_customCursor.transformed( transform, mode ); 0202 } else { 0203 // Calculate the scaled arrow shape 0204 const QPointF baseX( m_cursorSize, 0.0 ); 0205 const QPointF baseY( 0.0, m_cursorSize ); 0206 const QPointF relativeLeft = - ( baseX * 9 ) + ( baseY * 9 ); 0207 const QPointF relativeRight = ( baseX * 9 ) + ( baseY * 9 ); 0208 const QPointF relativeTip = - ( baseY * 19.0 ); 0209 m_arrow = QPolygonF() << QPointF( 0.0, 0.0 ) << relativeLeft << relativeTip << relativeRight; 0210 0211 // Rotate the shape according to the current direction and move it to the screen center 0212 QMatrix transformation; 0213 transformation.translate( screenPositionX, screenPositionY ); 0214 transformation.rotate( rotation ); 0215 m_arrow = m_arrow * transformation; 0216 0217 m_dirtyRegion = QRegion(); 0218 m_dirtyRegion += ( m_arrow.boundingRect().toRect() ); 0219 m_dirtyRegion += ( m_previousArrow.boundingRect().toRect() ); 0220 } 0221 0222 painter->save(); 0223 0224 GeoDataAccuracy accuracy = marbleModel()->positionTracking()->accuracy(); 0225 if ( accuracy.horizontal > 0 && accuracy.horizontal < 1000 ) { 0226 // Paint a circle indicating the position accuracy 0227 painter->setPen( Qt::transparent ); 0228 qreal planetRadius = m_marbleModel->planet()->radius(); 0229 int width = qRound( accuracy.horizontal * viewport->radius() / planetRadius ); 0230 if ( MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ) { 0231 int arrowSize = qMax<int>( m_arrow.boundingRect().width(), m_arrow.boundingRect().height() ); 0232 width = qMax<int>( width, arrowSize + 10 ); 0233 } 0234 0235 painter->setBrush( m_accuracyColor ); 0236 painter->drawEllipse( m_currentPosition, width, width ); 0237 } 0238 0239 // Draw trail if requested. 0240 if( m_showTrail ) { 0241 painter->save(); 0242 0243 // Use selected color to draw trail. 0244 painter->setBrush( m_trailColor ); 0245 painter->setPen( m_trailColor ); 0246 0247 // we don't draw m_trail[0] which is current position 0248 for( int i = 1; i < m_trail.size(); ++i ) { 0249 // Get screen coordinates from coordinates on the map. 0250 qreal trailPointX, trailPointY; 0251 viewport->screenCoordinates( m_trail[i], trailPointX, trailPointY ); 0252 0253 const int size = ( sm_numTrailPoints - i ) * 3; 0254 QRectF trailRect; 0255 trailRect.setX( trailPointX - size / 2.0 ); 0256 trailRect.setY( trailPointY - size / 2.0 ); 0257 trailRect.setWidth( size ); 0258 trailRect.setHeight( size ); 0259 0260 const qreal opacity = 1.0 - 0.15 * ( i - 1 ); 0261 painter->setOpacity( opacity ); 0262 painter->drawEllipse( trailRect ); 0263 } 0264 0265 painter->restore(); 0266 } 0267 0268 if( m_useCustomCursor) 0269 { 0270 painter->drawPixmap( m_currentPosition, m_customCursorTransformed ); 0271 } 0272 else 0273 { 0274 painter->setPen( Qt::black ); 0275 painter->setBrush( Qt::white ); 0276 painter->drawPolygon( m_arrow ); 0277 } 0278 0279 painter->restore(); 0280 m_previousArrow = m_arrow; 0281 } 0282 return true; 0283 } 0284 0285 QHash<QString,QVariant> PositionMarker::settings() const 0286 { 0287 QHash<QString, QVariant> settings = RenderPlugin::settings(); 0288 0289 settings.insert(QStringLiteral("useCustomCursor"), m_useCustomCursor); 0290 settings.insert(QStringLiteral("cursorPath"), m_cursorPath); 0291 settings.insert(QStringLiteral("cursorSize"), m_cursorSize); 0292 settings.insert(QStringLiteral("acColor"), m_accuracyColor); 0293 settings.insert(QStringLiteral("trailColor"), m_trailColor); 0294 settings.insert(QStringLiteral("showTrail"), m_showTrail); 0295 0296 return settings; 0297 } 0298 0299 void PositionMarker::setSettings( const QHash<QString, QVariant> &settings ) 0300 { 0301 RenderPlugin::setSettings( settings ); 0302 0303 const bool smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen; 0304 QColor defaultColor = Oxygen::brickRed4; 0305 defaultColor.setAlpha( smallScreen ? 80 : 40 ); 0306 0307 m_useCustomCursor = settings.value(QStringLiteral("useCustomCursor"), false).toBool(); 0308 m_cursorPath = settings.value(QStringLiteral("cursorPath"), m_defaultCursorPath).toString(); 0309 m_cursorSize = settings.value(QStringLiteral("cursorSize"), 1.0).toFloat(); 0310 loadCustomCursor( m_cursorPath, m_useCustomCursor ); 0311 0312 m_accuracyColor = settings.value(QStringLiteral("acColor"), defaultColor).value<QColor>(); 0313 m_trailColor = settings.value(QStringLiteral("trailColor"), QColor(0, 0, 255)).value<QColor>(); 0314 m_showTrail = settings.value(QStringLiteral("showTrail"), false).toBool(); 0315 0316 readSettings(); 0317 } 0318 0319 void PositionMarker::readSettings() 0320 { 0321 if ( !m_configDialog ) { 0322 return; 0323 } 0324 0325 if( m_useCustomCursor ) 0326 ui_configWidget->m_customCursor->click(); 0327 else 0328 ui_configWidget->m_originalCursor->click(); 0329 0330 bool found = false; 0331 float cursorSize = m_cursorSize; 0332 for( int i = 0; i < sm_numResizeSteps && !found; i++ ) 0333 { 0334 if( sm_resizeSteps[i] == cursorSize ) 0335 { 0336 ui_configWidget->m_resizeSlider->setValue( i ); 0337 found = true; 0338 } 0339 } 0340 if( !found ) 0341 { 0342 ui_configWidget->m_resizeSlider->setValue( sm_defaultSizeStep ); 0343 cursorSize = sm_resizeSteps[sm_defaultSizeStep]; 0344 } 0345 0346 ui_configWidget->m_sizeLabel->setText( tr( "Cursor Size: %1" ).arg( cursorSize ) ); 0347 QPalette palette = ui_configWidget->m_acColorChooserButton->palette(); 0348 palette.setColor( QPalette::Button, m_accuracyColor ); 0349 ui_configWidget->m_acColorChooserButton->setPalette( palette ); 0350 palette = ui_configWidget->m_trailColorChooserButton->palette(); 0351 palette.setColor( QPalette::Button, m_trailColor ); 0352 ui_configWidget->m_trailColorChooserButton->setPalette( palette ); 0353 ui_configWidget->m_trailCheckBox->setChecked( m_showTrail ); 0354 } 0355 0356 void PositionMarker::writeSettings() 0357 { 0358 if ( !m_configDialog ) { 0359 return; 0360 } 0361 0362 m_useCustomCursor = ui_configWidget->m_customCursor->isChecked(); 0363 m_cursorPath = m_cursorPath; 0364 m_cursorSize = sm_resizeSteps[ui_configWidget->m_resizeSlider->value()]; 0365 m_accuracyColor = m_accuracyColor; 0366 m_trailColor = m_trailColor; 0367 m_showTrail = ui_configWidget->m_trailCheckBox->isChecked(); 0368 0369 emit settingsChanged( nameId() ); 0370 } 0371 0372 void PositionMarker::setPosition( const GeoDataCoordinates &position ) 0373 { 0374 m_previousPosition = m_currentPosition; 0375 m_currentPosition = position; 0376 m_heading = marbleModel()->positionTracking()->direction(); 0377 // Update the trail 0378 m_trail.push_front( m_currentPosition ); 0379 for( int i = sm_numTrailPoints + 1; i< m_trail.size(); ++i ) { 0380 m_trail.pop_back(); 0381 } 0382 if ( m_lastBoundingBox.contains( m_currentPosition ) ) 0383 { 0384 emit repaintNeeded( m_dirtyRegion ); 0385 } 0386 } 0387 0388 void PositionMarker::chooseCustomCursor() 0389 { 0390 QString filename = QFileDialog::getOpenFileName( nullptr, tr( "Choose Custom Cursor" ) ); 0391 if( !filename.isEmpty() ) 0392 loadCustomCursor( filename, true ); 0393 } 0394 0395 void PositionMarker::loadCustomCursor( const QString& filename, bool useCursor ) 0396 { 0397 m_customCursor = QPixmap( filename ).scaled( 22 * m_cursorSize, 22 * m_cursorSize, Qt::KeepAspectRatio, Qt::SmoothTransformation ); 0398 if( !m_customCursor.isNull() ) 0399 { 0400 if( m_configDialog ) 0401 { 0402 if( useCursor ) 0403 ui_configWidget->m_customCursor->click(); 0404 ui_configWidget->m_fileChooserButton->setIconSize( QSize( m_customCursor.width(), m_customCursor.height() ) ); 0405 ui_configWidget->m_fileChooserButton->setIcon( QIcon( m_customCursor ) ); 0406 } 0407 m_cursorPath = filename; 0408 } 0409 else 0410 { 0411 mDebug() << "Unable to load custom cursor from " << filename << ". " 0412 << "The default cursor will be used instead"; 0413 if ( m_configDialog ) 0414 ui_configWidget->m_fileChooserButton->setIcon( QIcon( m_defaultCursor ) ); 0415 m_customCursor = m_defaultCursor; 0416 m_cursorPath = m_defaultCursorPath; 0417 } 0418 } 0419 0420 void PositionMarker::loadDefaultCursor() 0421 { 0422 m_defaultCursor = QPixmap( m_defaultCursorPath ).scaled( 22 * m_cursorSize, 22 * m_cursorSize, Qt::KeepAspectRatio, Qt::SmoothTransformation ); 0423 } 0424 0425 void PositionMarker::chooseColor() 0426 { 0427 QColor initialColor; 0428 if( sender() == ui_configWidget->m_acColorChooserButton ) { 0429 initialColor = m_accuracyColor; 0430 } 0431 else if( sender() == ui_configWidget->m_trailColorChooserButton ) { 0432 initialColor = m_trailColor; 0433 } 0434 QColor color = QColorDialog::getColor( initialColor, nullptr, 0435 tr( "Please choose a color" ), 0436 QColorDialog::ShowAlphaChannel ); 0437 if( color.isValid() ) 0438 { 0439 QPalette palette; 0440 if( sender() == ui_configWidget->m_acColorChooserButton ) { 0441 m_accuracyColor = color; 0442 palette = ui_configWidget->m_acColorChooserButton->palette(); 0443 palette.setColor( QPalette::Button, m_accuracyColor ); 0444 ui_configWidget->m_acColorChooserButton->setPalette( palette ); 0445 } 0446 else if( sender() == ui_configWidget->m_trailColorChooserButton ) { 0447 m_trailColor = color; 0448 palette = ui_configWidget->m_trailColorChooserButton->palette(); 0449 palette.setColor( QPalette::Button, m_trailColor ); 0450 ui_configWidget->m_trailColorChooserButton->setPalette( palette ); 0451 } 0452 } 0453 } 0454 0455 void PositionMarker::resizeCursor( int step ) 0456 { 0457 m_cursorSize = sm_resizeSteps[step]; 0458 float newSize = 22 * m_cursorSize; 0459 m_customCursor = QPixmap( m_cursorPath ).scaled( newSize, newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation ); 0460 ui_configWidget->m_sizeLabel->setText( tr( "Cursor Size: %1" ).arg( m_cursorSize ) ); 0461 if( !m_customCursor.isNull() ) 0462 { 0463 ui_configWidget->m_fileChooserButton->setIconSize( QSize( m_customCursor.width(), m_customCursor.height() ) ); 0464 ui_configWidget->m_fileChooserButton->setIcon( QIcon( m_customCursor ) ); 0465 } 0466 loadDefaultCursor(); 0467 } 0468 0469 qreal PositionMarker::zValue() const 0470 { 0471 return 1.0; 0472 } 0473 0474 } 0475 0476 #include "moc_PositionMarker.cpp"