void QgsGPSInformationWidget::disconnectGps() { if ( mLogFile && mLogFile->isOpen() ) { disconnect( mNmea, &QgsGPSConnection::nmeaSentenceReceived, this, &QgsGPSInformationWidget::logNmeaSentence ); mLogFile->close(); delete mLogFile; mLogFile = nullptr; } QgsApplication::gpsConnectionRegistry()->unregisterConnection( mNmea ); delete mNmea; mNmea = nullptr; if ( mpMapMarker ) // marker should not be shown on GPS disconnected - not current position { delete mpMapMarker; mpMapMarker = nullptr; } mGPSPlainTextEdit->appendPlainText( tr( "Disconnected..." ) ); mConnectButton->setChecked( false ); mConnectButton->setText( tr( "&Connect" ) ); showStatusBarMessage( tr( "Disconnected from GPS device." ) ); setStatusIndicator( NoData ); }
QgsGPSInformationWidget::QgsGPSInformationWidget( QgsMapCanvas * thepCanvas, QWidget * parent, Qt::WindowFlags f ) : QWidget( parent, f ) , mNmea( 0 ) , mpCanvas( thepCanvas ) { setupUi( this ); mpLastLayer = 0; mLastGpsPosition = QgsPoint( 0.0, 0.0 ); mpMapMarker = 0; mpRubberBand = 0; populateDevices(); QWidget * mpHistogramWidget = mStackedWidget->widget( 1 ); #if (!WITH_QWTPOLAR) mBtnSatellites->setVisible( false ); #endif // // Set up the graph for signal strength // mpPlot = new QwtPlot( mpHistogramWidget ); mpPlot->setAutoReplot( false ); // plot on demand //mpPlot->setTitle(QObject::tr("Signal Status")); //mpPlot->insertLegend(new QwtLegend(), QwtPlot::BottomLegend); // Set axis titles //mpPlot->setAxisTitle(QwtPlot::xBottom, QObject::tr("Satellite")); //mpPlot->setAxisTitle(QwtPlot::yLeft, QObject::tr("Value")); mpPlot->setAxisScale( QwtPlot::xBottom, 0, 20 ); mpPlot->setAxisScale( QwtPlot::yLeft, 0, 100 ); // max is 50dB SNR, I believe - SLM // add a grid //QwtPlotGrid * mypGrid = new QwtPlotGrid(); //mypGrid->attach( mpPlot ); //display satellites first mpCurve = new QwtPlotCurve(); mpCurve->setRenderHint( QwtPlotItem::RenderAntialiased ); mpCurve->setPen( QPen( Qt::blue ) ); mpCurve->setBrush( QBrush( Qt::blue ) ); mpPlot->enableAxis( QwtPlot::yLeft, false ); mpPlot->enableAxis( QwtPlot::xBottom, false ); mpCurve->attach( mpPlot ); //ensure all children get removed mpPlot->setAutoDelete( true ); QVBoxLayout *mpHistogramLayout = new QVBoxLayout( mpHistogramWidget ); mpHistogramLayout->setContentsMargins( 0, 0, 0, 0 ); mpHistogramLayout->addWidget( mpPlot ); mpHistogramWidget->setLayout( mpHistogramLayout ); // // Set up the polar graph for satellite pos // #if (WITH_QWTPOLAR) QWidget * mpPolarWidget = mStackedWidget->widget( 2 ); mpSatellitesWidget = new QwtPolarPlot( /*QwtText( tr( "Satellite View" ), QwtText::PlainText ),*/ mpPolarWidget ); // possible title for graph removed for now as it is too large in small windows mpSatellitesWidget->setAutoReplot( false ); // plot on demand (after all data has been handled) mpSatellitesWidget->setPlotBackground( Qt::white ); // scales mpSatellitesWidget->setScale( QwtPolar::ScaleAzimuth, 360, //min - reverse the min/max values to get compass orientation - increasing clockwise 0, //max 90 //interval - just show cardinal and intermediate (NE, N, NW, etc.) compass points (in degrees) ); mpSatellitesWidget->setAzimuthOrigin( M_PI_2 ); // to get compass orientation - need to rotate 90 deg. ccw; this is in Radians (not indicated in QwtPolarPlot docs) // mpSatellitesWidget->setScaleMaxMinor( QwtPolar::ScaleRadius, 2 ); // seems unnecessary mpSatellitesWidget->setScale( QwtPolar::ScaleRadius, 90, //min - reverse the min/max to get 0 at edge, 90 at center 0, //max 45 //interval ); // grids, axes QwtPolarGrid * mypSatellitesGrid = new QwtPolarGrid(); mypSatellitesGrid->setGridAttribute( QwtPolarGrid::AutoScaling, false ); // This fixes the issue of autoscaling on the Radius grid. It is ON by default AND is separate from the scaleData.doAutoScale in QwtPolarPlot::setScale(), etc. THIS IS VERY TRICKY! mypSatellitesGrid->setPen( QPen( Qt::black ) ); QPen minorPen( Qt::gray ); // moved outside of for loop; NOTE setting the minor pen isn't necessary if the minor grids aren't shown for ( int scaleId = 0; scaleId < QwtPolar::ScaleCount; scaleId++ ) { //mypSatellitesGrid->showGrid( scaleId ); //mypSatellitesGrid->showMinorGrid(scaleId); mypSatellitesGrid->setMinorGridPen( scaleId, minorPen ); } // mypSatellitesGrid->setAxisPen( QwtPolar::AxisAzimuth, QPen( Qt::black ) ); mypSatellitesGrid->showAxis( QwtPolar::AxisAzimuth, true ); mypSatellitesGrid->showAxis( QwtPolar::AxisLeft, false ); //alt axis mypSatellitesGrid->showAxis( QwtPolar::AxisRight, false );//alt axis mypSatellitesGrid->showAxis( QwtPolar::AxisTop, false );//alt axis mypSatellitesGrid->showAxis( QwtPolar::AxisBottom, false );//alt axis mypSatellitesGrid->showGrid( QwtPolar::ScaleAzimuth, false ); // hide the grid; just show ticks at edge mypSatellitesGrid->showGrid( QwtPolar::ScaleRadius, true ); // mypSatellitesGrid->showMinorGrid( QwtPolar::ScaleAzimuth, true ); mypSatellitesGrid->showMinorGrid( QwtPolar::ScaleRadius, true ); // for 22.5, 67.5 degree circles mypSatellitesGrid->attach( mpSatellitesWidget ); //QwtLegend *legend = new QwtLegend; //mpSatellitesWidget->insertLegend(legend, QwtPolarPlot::BottomLegend); QVBoxLayout *mpPolarLayout = new QVBoxLayout( mpPolarWidget ); mpPolarLayout->setContentsMargins( 0, 0, 0, 0 ); mpPolarLayout->addWidget( mpSatellitesWidget ); mpPolarWidget->setLayout( mpPolarLayout ); // replot on command mpSatellitesWidget->replot(); #endif mpPlot->replot(); // Restore state QSettings mySettings; mGroupShowMarker->setChecked( mySettings.value( "/gps/showMarker", "true" ).toBool() ); mSliderMarkerSize->setValue( mySettings.value( "/gps/markerSize", "12" ).toInt() ); mSpinTrackWidth->setValue( mySettings.value( "/gps/trackWidth", "2" ).toInt() ); mTrackColor = mySettings.value( "/gps/trackColor", QColor( Qt::red ) ).value<QColor>(); QString myPortMode = mySettings.value( "/gps/portMode", "scanPorts" ).toString(); mSpinMapExtentMultiplier->setValue( mySettings.value( "/gps/mapExtentMultiplier", "50" ).toInt() ); mDateTimeFormat = mySettings.value( "/gps/dateTimeFormat", "" ).toString(); // zero-length string signifies default format mGpsdHost->setText( mySettings.value( "/gps/gpsdHost", "localhost" ).toString() ); mGpsdPort->setText( mySettings.value( "/gps/gpsdPort", 2947 ).toString() ); mGpsdDevice->setText( mySettings.value( "/gps/gpsdDevice" ).toString() ); //port mode if ( myPortMode == "scanPorts" ) { mRadAutodetect->setChecked( true ); } else if ( myPortMode == "internalGPS" ) { mRadInternal->setChecked( true ); } else if ( myPortMode == "explicitPort" ) { mRadUserPath->setChecked( true ); } else if ( myPortMode == "gpsd" ) { mRadGpsd->setChecked( true ); } //disable the internal port method if build is without QtLocation #ifndef HAVE_QT_MOBILITY_LOCATION mRadInternal->setDisabled( true ); mRadAutodetect->setChecked( true ); #endif //auto digitising behaviour mCbxAutoAddVertices->setChecked( mySettings.value( "/gps/autoAddVertices", "false" ).toBool() ); mCbxAutoCommit->setChecked( mySettings.value( "/gps/autoCommit", "false" ).toBool() ); //pan mode QString myPanMode = mySettings.value( "/gps/panMode", "recenterWhenNeeded" ).toString(); if ( myPanMode == "none" ) { radNeverRecenter->setChecked( true ); } else if ( myPanMode == "recenterAlways" ) { radRecenterMap->setChecked( true ); } else { radRecenterWhenNeeded->setChecked( true ); } mWgs84CRS.createFromOgcWmsCrs( "EPSG:4326" ); mBtnDebug->setVisible( mySettings.value( "/gps/showDebug", "false" ).toBool() ); // use a registry setting to control - power users/devs could set it // status = unknown setStatusIndicator( NoData ); //SLM - added functionality mLogFile = 0; connect( QgisApp::instance()->layerTreeView(), SIGNAL( currentLayerChanged( QgsMapLayer* ) ), this, SLOT( updateCloseFeatureButton( QgsMapLayer* ) ) ); mStackedWidget->setCurrentIndex( 3 ); // force to Options mBtnPosition->setFocus( Qt::TabFocusReason ); }
void QgsGPSInformationWidget::displayGPSInformation( const QgsGPSInformation &info ) { QVector<QPointF> data; // set validity flag and status from GPS data // based on GGA, GSA and RMC sentences - the logic does not require all bool validFlag = false; // true if GPS indicates position fix FixStatus fixStatus = NoData; // no fix if any of the three report bad; default values are invalid values and won't be changed if the corresponding NMEA msg is not received if ( info.status == 'V' || info.fixType == NMEA_FIX_BAD || info.quality == 0 ) // some sources say that 'V' indicates position fix, but is below acceptable quality { fixStatus = NoFix; } else if ( info.fixType == NMEA_FIX_2D ) // 2D indication (from GGA) { fixStatus = Fix2D; validFlag = true; } else if ( info.status == 'A' || info.fixType == NMEA_FIX_3D || info.quality > 0 ) // good { fixStatus = Fix3D; validFlag = true; } else // unknown status (not likely) { } // set visual status indicator -- do only on change of state if ( fixStatus != mLastFixStatus ) { setStatusIndicator( fixStatus ); } if ( mStackedWidget->currentIndex() == 1 && info.satInfoComplete ) //signal { mpPlot->setAxisScale( QwtPlot::xBottom, 0, info.satellitesInView.size() ); } //signal #ifdef WITH_QWTPOLAR if ( mStackedWidget->currentIndex() == 2 && info.satInfoComplete ) //satellites { while ( !mMarkerList.isEmpty() ) { delete mMarkerList.takeFirst(); } } //satellites #endif if ( mStackedWidget->currentIndex() == 4 ) //debug { mGPSPlainTextEdit->clear(); } //debug for ( int i = 0; i < info.satellitesInView.size(); ++i ) //satellite processing loop { QgsSatelliteInfo currentInfo = info.satellitesInView.at( i ); if ( mStackedWidget->currentIndex() == 1 && info.satInfoComplete ) //signal { data << QPointF( i, 0 ); data << QPointF( i, currentInfo.signal ); data << QPointF( i + 1, currentInfo.signal ); data << QPointF( i + 1, 0 ); } //signal if ( mStackedWidget->currentIndex() == 2 && info.satInfoComplete ) //satellites { QColor bg( Qt::white ); // moved several items outside of the following if block to minimize loop time bg.setAlpha( 200 ); QColor myColor; // Add a marker to the polar plot if ( currentInfo.id > 0 ) // don't show satellite if id=0 (no satellite indication) { #ifdef WITH_QWTPOLAR QwtPolarMarker *mypMarker = new QwtPolarMarker(); #if (QWT_POLAR_VERSION<0x010000) mypMarker->setPosition( QwtPolarPoint( currentInfo.azimuth, currentInfo.elevation ) ); #else mypMarker->setPosition( QwtPointPolar( currentInfo.azimuth, currentInfo.elevation ) ); #endif #endif if ( currentInfo.signal < 30 ) //weak signal { myColor = Qt::red; } else { myColor = Qt::black; //strong signal } #ifdef WITH_QWTPOLAR QBrush symbolBrush( Qt::black ); QSize markerSize( 9, 9 ); QBrush textBgBrush( bg ); #if (QWT_POLAR_VERSION<0x010000) mypMarker->setSymbol( QwtSymbol( QwtSymbol::Ellipse, symbolBrush, QPen( myColor ), markerSize ) ); #else mypMarker->setSymbol( new QwtSymbol( QwtSymbol::Ellipse, symbolBrush, QPen( myColor ), markerSize ) ); #endif mypMarker->setLabelAlignment( Qt::AlignHCenter | Qt::AlignTop ); QwtText text( QString::number( currentInfo.id ) ); text.setColor( myColor ); text.setBackgroundBrush( textBgBrush ); mypMarker->setLabel( text ); mypMarker->attach( mpSatellitesWidget ); mMarkerList << mypMarker; #endif } // currentInfo.id > 0 } //satellites } //satellite processing loop if ( mStackedWidget->currentIndex() == 1 && info.satInfoComplete ) //signal { mpCurve->setSamples( data ); mpPlot->replot(); } //signal #ifdef WITH_QWTPOLAR if ( mStackedWidget->currentIndex() == 2 && info.satInfoComplete ) //satellites { mpSatellitesWidget->replot(); } //satellites #endif if ( validFlag ) { validFlag = info.longitude >= -180.0 && info.longitude <= 180.0 && info.latitude >= -90.0 && info.latitude <= 90.0; } QgsPointXY myNewCenter; if ( validFlag ) { myNewCenter = QgsPointXY( info.longitude, info.latitude ); } else { myNewCenter = mLastGpsPosition; } if ( mStackedWidget->currentIndex() == 0 ) //position { mTxtLatitude->setText( QString::number( info.latitude, 'f', 8 ) ); mTxtLongitude->setText( QString::number( info.longitude, 'f', 8 ) ); mTxtAltitude->setText( tr( "%1 m" ).arg( info.elevation, 0, 'f', 1 ) ); // don't know of any GPS receivers that output better than 0.1 m precision if ( mDateTimeFormat.isEmpty() ) { mTxtDateTime->setText( info.utcDateTime.toString( Qt::TextDate ) ); // default format } else { mTxtDateTime->setText( info.utcDateTime.toString( mDateTimeFormat ) ); //user specified format string for testing the millisecond part of time } mTxtSpeed->setText( tr( "%1 km/h" ).arg( info.speed, 0, 'f', 1 ) ); mTxtDirection->setText( QString::number( info.direction, 'f', 1 ) + QStringLiteral( "°" ) ); mTxtHdop->setText( QString::number( info.hdop, 'f', 1 ) ); mTxtVdop->setText( QString::number( info.vdop, 'f', 1 ) ); mTxtPdop->setText( QString::number( info.pdop, 'f', 1 ) ); mTxtHacc->setText( QString::number( info.hacc, 'f', 1 ) + "m" ); mTxtVacc->setText( QString::number( info.vacc, 'f', 1 ) + "m" ); mTxtFixMode->setText( info.fixMode == 'A' ? tr( "Automatic" ) : info.fixMode == 'M' ? tr( "Manual" ) : QLatin1String( "" ) ); // A=automatic 2d/3d, M=manual; allowing for anything else mTxtFixType->setText( info.fixType == 3 ? tr( "3D" ) : info.fixType == 2 ? tr( "2D" ) : info.fixType == 1 ? tr( "No fix" ) : QString::number( info.fixType ) ); // 1=no fix, 2=2D, 3=3D; allowing for anything else mTxtQuality->setText( info.quality == 2 ? tr( "Differential" ) : info.quality == 1 ? tr( "Non-differential" ) : info.quality == 0 ? tr( "No position" ) : info.quality > 2 ? QString::number( info.quality ) : QLatin1String( "" ) ); // allowing for anything else mTxtSatellitesUsed->setText( QString::number( info.satellitesUsed ) ); mTxtStatus->setText( info.status == 'A' ? tr( "Valid" ) : info.status == 'V' ? tr( "Invalid" ) : QLatin1String( "" ) ); } //position // Avoid refreshing / panning if we haven't moved if ( mLastGpsPosition != myNewCenter ) { mLastGpsPosition = myNewCenter; // Pan based on user specified behavior if ( radRecenterMap->isChecked() || radRecenterWhenNeeded->isChecked() ) { QgsCoordinateReferenceSystem mypSRS = mpCanvas->mapSettings().destinationCrs(); QgsCoordinateTransform myTransform( mWgs84CRS, mypSRS ); // use existing WGS84 CRS QgsPointXY myPoint = myTransform.transform( myNewCenter ); //keep the extent the same just center the map canvas in the display so our feature is in the middle QgsRectangle myRect( myPoint, myPoint ); // empty rect can be used to set new extent that is centered on the point used to construct the rect // testing if position is outside some proportion of the map extent // this is a user setting - useful range: 5% to 100% (0.05 to 1.0) QgsRectangle myExtentLimit( mpCanvas->extent() ); myExtentLimit.scale( mSpinMapExtentMultiplier->value() * 0.01 ); // only change the extents if the point is beyond the current extents to minimize repaints if ( radRecenterMap->isChecked() || ( radRecenterWhenNeeded->isChecked() && !myExtentLimit.contains( myPoint ) ) ) { mpCanvas->setExtent( myRect ); mpCanvas->refresh(); } } //otherwise never recenter automatically if ( mCbxAutoAddVertices->isChecked() ) { addVertex(); } } // mLastGpsPosition != myNewCenter // new marker position after recentering if ( mGroupShowMarker->isChecked() ) // show marker { if ( validFlag ) // update cursor position if valid position { // initially, cursor isn't drawn until first valid fix; remains visible until GPS disconnect if ( ! mpMapMarker ) { mpMapMarker = new QgsGpsMarker( mpCanvas ); } mpMapMarker->setSize( mSliderMarkerSize->value() ); mpMapMarker->setCenter( myNewCenter ); } } else { if ( mpMapMarker ) { delete mpMapMarker; mpMapMarker = nullptr; } } // show marker }