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 );
}
Beispiel #2
0
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
}