patch_for_4071.diff

patch - round 1 - Steven Mizuno, 2011-08-10 07:59 AM

Download (119 KB)

View differences:

src/app/gps/qgsgpsinformationwidget.cpp
18 18
#include "qgsnmeaconnection.h"
19 19
#include "qgsgpsconnectionregistry.h"
20 20
#include "qgsgpsdetector.h"
21
#include "info.h"
21 22
#include "qgscoordinatetransform.h"
22 23
#include <qgspoint.h>
23 24
#include <qgsrubberband.h>
......
29 30
#include "qgslogger.h"
30 31
#include "qgsfeatureaction.h"
31 32
#include "qgsgeometry.h"
33
#include <qgisapp.h>
32 34

  
33 35
//for avoid intersections static method
34 36
#include "qgsmaptooladdfeature.h"
......
39 41
#include <qwt_plot.h>
40 42
#include <qwt_plot_grid.h>
41 43
// QWT Polar plot add on
42
#include <qpen.h>
43 44
#include <qwt_data.h>
44 45
#include <qwt_symbol.h>
45 46
#include <qwt_polar_grid.h>
......
49 50
#include <QMessageBox>
50 51
#include <QSettings>
51 52
#include <QFileInfo>
52
#include <QPointF>
53
//#include <QPointF>
53 54
#include <QColorDialog>
54

  
55
#include <QFileDialog>
56
#include <QPixmap>
55 57

  
56 58
QgsGPSInformationWidget::QgsGPSInformationWidget( QgsMapCanvas * thepCanvas, QWidget * parent, Qt::WindowFlags f ):
57 59
    QWidget( parent, f ),
......
59 61
    mpCanvas( thepCanvas )
60 62
{
61 63
  setupUi( this );
64

  
65
  // to connect signals that layers have changed (which layer, edit state)
66
  mpLegend = QgisApp::instance()->legend();
67
  mpLastLayer = 0;
68

  
69
  mLastGpsPosition = QgsPoint( 0.0, 0.0 );
70

  
62 71
  mpMapMarker = 0;
63 72
  mpRubberBand = 0;
64 73
  populateDevices();
......
67 76
  // Set up the graph for signal strength
68 77
  //
69 78
  mpPlot = new QwtPlot( mpHistogramWidget );
79
  mpPlot->setAutoReplot( false );   // plot on demand
70 80
  //mpPlot->setTitle(QObject::tr("Signal Status"));
71 81
  //mpPlot->insertLegend(new QwtLegend(), QwtPlot::BottomLegend);
72 82
  // Set axis titles
73 83
  //mpPlot->setAxisTitle(QwtPlot::xBottom, QObject::tr("Satellite"));
74 84
  //mpPlot->setAxisTitle(QwtPlot::yLeft, QObject::tr("Value"));
75 85
  mpPlot->setAxisScale( QwtPlot::xBottom, 0, 20 );
76
  mpPlot->setAxisScale( QwtPlot::yLeft, 0, 100 );
86
  mpPlot->setAxisScale( QwtPlot::yLeft, 0, 100 );  // max is 50dB SNR, I believe - SLM
77 87
  // add a grid
78 88
  //QwtPlotGrid * mypGrid = new QwtPlotGrid();
79 89
  //mypGrid->attach( mpPlot );
......
96 106
  // Set up the polar graph for satellite pos
97 107
  //
98 108
  QWidget * mpPolarWidget = mStackedWidget->widget( 2 );
99
  //mpSatellitesWidget = new QwtPolarPlot(QwtText(tr("Satellite Positions")), myTab );
100
  mpSatellitesWidget = new QwtPolarPlot( mpPolarWidget );
101
  mpSatellitesWidget->setAutoReplot( true );
109
  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
110
  mpSatellitesWidget->setAutoReplot( false  );  // plot on demand (after all data has been handled)
102 111
  mpSatellitesWidget->setPlotBackground( Qt::white );
103 112
  // scales
104
  mpSatellitesWidget->setScale( QwtPolar::Azimuth,
105
                                0, //min
106
                                360, //max
107
                                30 //interval
113
  mpSatellitesWidget->setScale( QwtPolar::ScaleAzimuth,
114
                                360, //min - reverse the min/max values to get compass orientation - increasing clockwise
115
                                0, //max
116
                                90 //interval - just show cardinal and intermediate (NE, N, NW, etc.) compass points (in degrees)
108 117
                              );
118
  mpSatellitesWidget->setAzimuthOrigin( M_PI_2 );    // to get compass orientation - need to rotate 90 deg. ccw; this is in Radians (not indicated in QwtPolarPlot docs)
109 119

  
110
  mpSatellitesWidget->setScaleMaxMinor( QwtPolar::Azimuth, 10 );
111
  mpSatellitesWidget->setScale( QwtPolar::Radius,
112
                                0, //min
113
                                100 //max
120
//  mpSatellitesWidget->setScaleMaxMinor( QwtPolar::ScaleRadius, 2 );  // seems unnecessary
121
  mpSatellitesWidget->setScale( QwtPolar::ScaleRadius,
122
                                90, //min - reverse the min/max to get 0 at edge, 90 at center
123
                                0, //max
124
                                45 //interval
114 125
                              );
115 126

  
116 127
  // grids, axes
117 128

  
118 129
  QwtPolarGrid * mypSatellitesGrid = new QwtPolarGrid();
130
  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!
119 131
  mypSatellitesGrid->setPen( QPen( Qt::black ) );
132
  QPen minorPen( Qt::gray );  // moved outside of for loop; NOTE setting the minor pen isn't necessary if the minor grids aren't shown
120 133
  for ( int scaleId = 0; scaleId < QwtPolar::ScaleCount; scaleId++ )
121 134
  {
122
    mypSatellitesGrid->showGrid( scaleId );
135
    //mypSatellitesGrid->showGrid( scaleId );
123 136
    //mypSatellitesGrid->showMinorGrid(scaleId);
124
    QPen minorPen( Qt::gray );
125 137
    mypSatellitesGrid->setMinorGridPen( scaleId, minorPen );
126 138
  }
127
  mypSatellitesGrid->setAxisPen( QwtPolar::AxisAzimuth, QPen( Qt::black ) );
139
//  mypSatellitesGrid->setAxisPen( QwtPolar::AxisAzimuth, QPen( Qt::black ) );
128 140

  
129 141
  mypSatellitesGrid->showAxis( QwtPolar::AxisAzimuth, true );
130 142
  mypSatellitesGrid->showAxis( QwtPolar::AxisLeft, false ); //alt axis
131 143
  mypSatellitesGrid->showAxis( QwtPolar::AxisRight, false );//alt axis
132 144
  mypSatellitesGrid->showAxis( QwtPolar::AxisTop, false );//alt axis
133 145
  mypSatellitesGrid->showAxis( QwtPolar::AxisBottom, false );//alt axis
134
  mypSatellitesGrid->showGrid( QwtPolar::Azimuth, true );
135
  mypSatellitesGrid->showGrid( QwtPolar::Radius, true );
146
  mypSatellitesGrid->showGrid( QwtPolar::ScaleAzimuth, false ); // hide the grid; just show ticks at edge
147
  mypSatellitesGrid->showGrid( QwtPolar::ScaleRadius, true );
148
//  mypSatellitesGrid->showMinorGrid( QwtPolar::ScaleAzimuth, true );
149
  mypSatellitesGrid->showMinorGrid( QwtPolar::ScaleRadius, true );   // for 22.5, 67.5 degree circles
136 150
  mypSatellitesGrid->attach( mpSatellitesWidget );
137 151

  
138 152
  //QwtLegend *legend = new QwtLegend;
......
142 156
  mpPolarLayout->addWidget( mpSatellitesWidget );
143 157
  mpPolarWidget->setLayout( mpPolarLayout );
144 158

  
159
  // replot on command
160
  mpSatellitesWidget->replot();
161
  mpPlot->replot();
145 162

  
146 163
  // Restore state
147 164
  QSettings mySettings;
165
  mGroupShowMarker->setChecked(  mySettings.value( "/gps/showMarker", "true" ).toBool() );
148 166
  mSliderMarkerSize->setValue( mySettings.value( "/gps/markerSize", "12" ).toInt() );
149 167
  mSpinTrackWidth->setValue( mySettings.value( "/gps/trackWidth", "2" ).toInt() );
168
  mTrackColour = mySettings.value( "/gps/trackColour", QColor( Qt::red ) ).value<QColor>();
150 169
  QString myPortMode = mySettings.value( "/gps/portMode", "scanPorts" ).toString();
151 170

  
171
  mSpinMapExtentMultiplier->setValue( mySettings.value( "/gps/mapExtentMultiplier", "50" ).toInt() );
172
  mDateTimeFormat = mySettings.value( "/gps/dateTimeFormat", "" ).toString(); // zero-length string signifies default format
173

  
152 174
  mGpsdHost->setText( mySettings.value( "/gps/gpsdHost", "localhost" ).toString() );
153 175
  mGpsdPort->setText( mySettings.value( "/gps/gpsdPort", 2947 ).toString() );
154 176
  mGpsdDevice->setText( mySettings.value( "/gps/gpsdDevice" ).toString() );
......
166 188
  {
167 189
    mRadGpsd->setChecked( true );
168 190
  }
191

  
169 192
  //auto digitising behaviour
170
  bool myAutoAddVertexFlag = mySettings.value( "/gps/autoAddVertices", "false" ).toBool();
171
  mCbxAutoAddVertices->setChecked( myAutoAddVertexFlag );
193
  mCbxAutoAddVertices->setChecked( mySettings.value( "/gps/autoAddVertices", "false" ).toBool() );
194

  
195
  mCbxAutoCommit->setChecked( mySettings.value( "/gps/autoCommit", "false" ).toBool() );
196

  
172 197
  //pan mode
173 198
  QString myPanMode = mySettings.value( "/gps/panMode", "recenterWhenNeeded" ).toString();
174 199
  if ( myPanMode == "none" )
......
184 209
    radRecenterWhenNeeded->setChecked( true );
185 210
  }
186 211
  // Set up the rubber band to show digitising
187
  createRubberBand();
212

  
188 213
  mWgs84CRS.createFromOgcWmsCrs( "EPSG:4326" );
189
  //for now I am hiding accuracy and date widgets
190
  mDateTime->hide();
191
  mVerticalAccuracy->hide();
192
  mHorizontalAccuracy->hide();
193
  mBtnDebug->hide();
214

  
215
  mBtnDebug->setVisible(mySettings.value( "/gps/showDebug", "false" ).toBool());  // use a registry setting to control - power users/devs could set it
216

  
217
  // status = unknown
218
  setStatusIndicator( NoData );
219

  
220
  //SLM - added functionality
221
  mLogFile = 0;
222

  
223
  connect( mpLegend, SIGNAL( currentLayerChanged( QgsMapLayer* ) ),
224
           this, SLOT( updateCloseFeatureButton( QgsMapLayer* ) ) );
225

  
226
  mStackedWidget->setCurrentIndex( 3 ); // force to Options
227
  mBtnPosition->setFocus( Qt::TabFocusReason );
194 228
}
195 229

  
196 230
QgsGPSInformationWidget::~QgsGPSInformationWidget()
197 231
{
232
  if ( mNmea )
233
  {
234
      disconnectGps();
235
  }
236

  
198 237
  if ( mpMapMarker )
199 238
    delete mpMapMarker;
200 239

  
201 240
  QSettings mySettings;
202 241
  mySettings.setValue( "/gps/lastPort", mCboDevices->itemData( mCboDevices->currentIndex() ).toString() );
203 242
  mySettings.setValue( "/gps/trackWidth", mSpinTrackWidth->value() );
243
  mySettings.setValue( "/gps/trackColour", mTrackColour );
204 244
  mySettings.setValue( "/gps/markerSize", mSliderMarkerSize->value() );
245
  mySettings.setValue( "/gps/showMarker", mGroupShowMarker->isChecked() );
205 246
  mySettings.setValue( "/gps/autoAddVertices", mCbxAutoAddVertices->isChecked() );
247
  mySettings.setValue( "/gps/autoCommit", mCbxAutoCommit->isChecked() );
248

  
249
  mySettings.setValue( "/gps/mapExtentMultiplier", mSpinMapExtentMultiplier->value() );
250

  
206 251
  // scan, explicit port or gpsd
207 252
  if ( mRadAutodetect->isChecked() )
208 253
  {
......
243 288

  
244 289
void QgsGPSInformationWidget::on_mSpinTrackWidth_valueChanged( int theValue )
245 290
{
246
  QSettings mySettings;
247
  mySettings.setValue( "/gps/trackWidth", theValue );
248 291
  if ( mpRubberBand )
249 292
  {
250 293
    mpRubberBand->setWidth( theValue );
......
253 296

  
254 297
void QgsGPSInformationWidget::on_mBtnTrackColour_clicked( )
255 298
{
256
  QSettings mySettings;
257
  QColor myColor( mySettings.value( "/qgis/gps/line_color_red", 255 ).toInt(),
258
                  mySettings.value( "/qgis/gps/line_color_green", 0 ).toInt(),
259
                  mySettings.value( "/qgis/gps/line_color_blue", 0 ).toInt() );
260
  myColor = QColorDialog::getColor( myColor, this );
261
  mySettings.setValue( "/qgis/gps/line_color_red", myColor.red() );
262
  mySettings.setValue( "/qgis/gps/line_color_green", myColor.green() );
263
  mySettings.setValue( "/qgis/gps/line_color_blue", myColor.blue() );
264
  setTrackColour();
299
  QColor myColor = QColorDialog::getColor( mTrackColour, this );
300
  if ( myColor.isValid() )  // check that a color was picked
301
  {
302
    mTrackColour = myColor;
303
    if ( mpRubberBand )
304
    {
305
      mpRubberBand->setColor( myColor );
306
    }
307
  }
265 308
}
266 309

  
267

  
310
#if 0 //no longer used
268 311
void QgsGPSInformationWidget::setTrackColour( )
269 312
{
270 313
  QSettings mySettings;
......
276 319
    mpRubberBand->setColor( myColor );
277 320
  }
278 321
}
322
#endif
323

  
279 324
void QgsGPSInformationWidget::on_mBtnPosition_clicked( )
280 325
{
281 326
  mStackedWidget->setCurrentIndex( 0 );
327
  if ( mNmea ) displayGPSInformation( mNmea->currentGPSInformation() );
282 328
}
283 329

  
284 330
void QgsGPSInformationWidget::on_mBtnSignal_clicked( )
285 331
{
286 332
  mStackedWidget->setCurrentIndex( 1 );
333
  if ( mNmea ) displayGPSInformation( mNmea->currentGPSInformation() );
287 334
}
288 335

  
289 336
void QgsGPSInformationWidget::on_mBtnSatellites_clicked( )
290 337
{
291 338
  mStackedWidget->setCurrentIndex( 2 );
339
  if ( mNmea ) displayGPSInformation( mNmea->currentGPSInformation() );
292 340
}
293 341

  
294 342
void QgsGPSInformationWidget::on_mBtnOptions_clicked( )
......
315 363

  
316 364
void QgsGPSInformationWidget::connectGps()
317 365
{
366
  // clear position page fields to give better indication that something happened (or didn't happen)
367
  mTxtLatitude->clear();
368
  mTxtLongitude->clear();
369
  mTxtAltitude->clear();
370
  mTxtDateTime->clear();
371
  mTxtSpeed->clear();
372
  mTxtDirection->clear();
373
  mTxtHdop->clear();
374
  mTxtVdop->clear();
375
  mTxtPdop->clear();
376
  mTxtFixMode->clear();
377
  mTxtFixType->clear();
378
  mTxtQuality->clear();
379
  mTxtSatellitesUsed->clear();
380
  mTxtStatus->clear();
381

  
382
  mLastGpsPosition = QgsPoint( 0.0, 0.0 );
383

  
318 384
  QString port;
319 385

  
320 386
  if ( mRadUserPath->isChecked() )
......
335 401
    port = QString( "%1:%2:%3" ).arg( mGpsdHost->text() ).arg( mGpsdPort->text() ).arg( mGpsdDevice->text() );
336 402
  }
337 403

  
338
  mGPSTextEdit->append( tr( "Connecting..." ) );
404
  mGPSPlainTextEdit->appendPlainText( tr( "Connecting..." ) );
405
  showStatusBarMessage( tr( "Connecting to GPS device..." ) );
339 406

  
340 407
  QgsGPSDetector *detector = new QgsGPSDetector( port );
341 408
  connect( detector, SIGNAL( detected( QgsGPSConnection * ) ), this, SLOT( connected( QgsGPSConnection * ) ) );
342 409
  connect( detector, SIGNAL( detectionFailed() ), this, SLOT( timedout() ) );
410
  detector->advance();   // start the detection process
343 411
}
344 412

  
345 413
void QgsGPSInformationWidget::timedout()
346 414
{
347 415
  mConnectButton->setChecked( false );
348 416
  mNmea = NULL;
349
  mGPSTextEdit->append( tr( "Timed out!" ) );
417
  mGPSPlainTextEdit->appendPlainText( tr( "Timed out!" ) );
418
  showStatusBarMessage( tr( "Failed to connect to GPS device." ) );
350 419
}
351 420

  
352 421
void QgsGPSInformationWidget::connected( QgsGPSConnection *conn )
......
354 423
  mNmea = conn;
355 424
  QObject::connect( mNmea, SIGNAL( stateChanged( const QgsGPSInformation& ) ),
356 425
                    this, SLOT( displayGPSInformation( const QgsGPSInformation& ) ) );
357
  mGPSTextEdit->append( tr( "Connected!" ) );
358
  mConnectButton->setText( tr( "Disconnect" ) );
426
  mGPSPlainTextEdit->appendPlainText( tr( "Connected!" ) );
427
  mConnectButton->setText( tr( "Dis&connect" ) );
359 428
  //insert connection into registry such that it can also be used by other dialogs or plugins
360 429
  QgsGPSConnectionRegistry::instance()->registerConnection( mNmea );
430
  showStatusBarMessage( tr( "Connected to GPS device." ) );
431

  
432
  if ( mLogFileGroupBox->isChecked() && ! mTxtLogFile->text().isEmpty() )
433
  {
434
    if ( ! mLogFile )
435
    {
436
      mLogFile = new QFile( mTxtLogFile->text() );
437
    }
438

  
439
    if ( mLogFile->open( QIODevice::Append ) )  // open in binary and explicitly output CR + LF per NMEA
440
    {
441
      mLogFileTextStream.setDevice( mLogFile );
442

  
443
      // crude way to separate chunks - use when manually editing file - NMEA parsers should discard
444
      mLogFileTextStream << "====" << "\r\n";
445

  
446
      QObject::connect( mNmea, SIGNAL( nmeaSentenceReceived( const QString& ) ), this, SLOT( logNmeaSentence( const QString& ) ) ); // added to handle raw data
447
    }
448
    else  // error opening file
449
    {
450
      delete mLogFile;
451
      mLogFile = 0;
452

  
453
      // need to indicate why - this just reports that an error occurred
454
      showStatusBarMessage( tr( "Error opening log file." ) );
455
    }
456
  }
361 457
}
362 458

  
363 459
void QgsGPSInformationWidget::disconnectGps()
364 460
{
461
  if ( mLogFile && mLogFile->isOpen() )
462
  {
463
    QObject::disconnect( mNmea, SIGNAL( nmeaSentenceReceived( const QString& ) ), this, SLOT( logNmeaSentence( const QString& ) ) );
464
    mLogFile->close();
465
    delete mLogFile;
466
    mLogFile = 0;
467
  }
468

  
365 469
  QgsGPSConnectionRegistry::instance()->unregisterConnection( mNmea );
366 470
  delete mNmea;
367
  mGPSTextEdit->append( tr( "Disconnected..." ) );
471
  mNmea = NULL;
472
  if ( mpMapMarker )  // marker should not be shown on GPS disconnected - not current position
473
  {
474
    delete mpMapMarker;
475
    mpMapMarker = NULL;
476
  }
477
  mGPSPlainTextEdit->appendPlainText( tr( "Disconnected..." ) );
368 478
  mConnectButton->setChecked( false );
369
  mConnectButton->setText( tr( "Connect" ) );
370
}
479
  mConnectButton->setText( tr( "&Connect" ) );
480
  showStatusBarMessage( tr( "Disconnected from GPS device." ) );
371 481

  
482
  setStatusIndicator( NoData );
483

  
484
}
372 485

  
373 486
void QgsGPSInformationWidget::displayGPSInformation( const QgsGPSInformation& info )
374 487
{
375
  mGPSTextEdit->clear();
376

  
377 488
  QwtArray<double> myXData;//qwtarray is just a wrapped qvector
378 489
  QwtArray<double> mySignalData;//qwtarray is just a wrapped qvector
379
  mpPlot->setAxisScale( QwtPlot::xBottom, 0, info.satellitesInView.size() );
380
  while ( !mMarkerList.isEmpty() )
490

  
491
  // set validity flag and status from GPS data
492
  // based on GGA, GSA and RMC sentences - the logic does not require all
493
  bool validFlag = false; // true if GPS indicates position fix
494
  FixStatus fixStatus = NoData;
495

  
496
  // 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
497
  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
498
  {
499
    fixStatus = NoFix;
500
  }
501
  else if ( info.fixType == NMEA_FIX_2D ) // 2D indication (from GGA)
502
  {
503
    fixStatus = Fix2D;
504
    validFlag = true;
505
  }
506
  else if ( info.status == 'A' || info.fixType == NMEA_FIX_3D || info.quality > 0 ) // good
507
  {
508
    fixStatus = Fix3D;
509
    validFlag = true;
510
  }
511
  else  // unknown status (not likely)
381 512
  {
382
    delete mMarkerList.takeFirst();
383 513
  }
384 514

  
385
  for ( int i = 0; i < info.satellitesInView.size(); ++i )
515
  // set visual status indicator -- do only on change of state
516
  if ( fixStatus != mLastFixStatus )
386 517
  {
387
    QgsSatelliteInfo currentInfo = info.satellitesInView.at( i );
518
    setStatusIndicator( fixStatus );
519
  }
388 520

  
389
    myXData.append( i );
390
    mySignalData.append( 0 );
391
    myXData.append( i );
392
    mySignalData.append( currentInfo.signal );
393
    myXData.append( i + 1 );
394
    mySignalData.append( currentInfo.signal );
395
    myXData.append( i + 1 );
396
    mySignalData.append( 0 );
397
    mGPSTextEdit->append( "Satellite" );
398
    if ( currentInfo.inUse )
521
  if ( mStackedWidget->currentIndex() == 1 && info.satInfoComplete ) //signal
522
  {
523
    mpPlot->setAxisScale( QwtPlot::xBottom, 0, info.satellitesInView.size() );
524
  } //signal
525

  
526
  if ( mStackedWidget->currentIndex() == 2 && info.satInfoComplete ) //satellites
527
  {
528
    while ( !mMarkerList.isEmpty() )
399 529
    {
400
      mGPSTextEdit->append( "In use" );
530
      delete mMarkerList.takeFirst();
401 531
    }
402
    else
532
  } //satellites
533

  
534
  if ( mStackedWidget->currentIndex() == 4 ) //debug
535
  {
536
    mGPSPlainTextEdit->clear();
537
  } //debug
538

  
539
  for ( int i = 0; i < info.satellitesInView.size(); ++i ) //satellite processing loop
540
  {
541
    QgsSatelliteInfo currentInfo = info.satellitesInView.at( i );
542

  
543
    if ( mStackedWidget->currentIndex() == 1 && info.satInfoComplete ) //signal
403 544
    {
404
      mGPSTextEdit->append( "Not in use" );
405
    }
406
    mGPSTextEdit->append( "id: " + QString::number( currentInfo.id ) );
407
    mGPSTextEdit->append( "elevation: " + QString::number( currentInfo.elevation ) );
408
    mGPSTextEdit->append( "azimuth: " + QString::number( currentInfo.azimuth ) );
409
    mGPSTextEdit->append( "signal: " + QString::number( currentInfo.signal ) );
410
    // Add a marker to the polar plot
411
    QwtPolarMarker *mypMarker = new QwtPolarMarker();
412
    mypMarker->setPosition( QwtPolarPoint( currentInfo.azimuth, currentInfo.elevation ) );
413
    QColor myColour;
414
    if ( currentInfo.signal < 30 ) //weak signal
545
      myXData.append( i );
546
      mySignalData.append( 0 );
547
      myXData.append( i );
548
      mySignalData.append( currentInfo.signal );
549
      myXData.append( i + 1 );
550
      mySignalData.append( currentInfo.signal );
551
      myXData.append( i + 1 );
552
      mySignalData.append( 0 );
553
    } //signal
554

  
555
#if 0
556
    if ( mStackedWidget->currentIndex() == 4 ) //debug
415 557
    {
416
      myColour = Qt::red;
417
    }
418
    else
558
#if 0   // temporarily removed as the info isn't being set in GSA/GSV processing
559
      //    mGPSPlainTextEdit->appendPlainText( "Satellite" );
560
      if ( currentInfo.inUse )
561
      {
562
        mGPSPlainTextEdit->appendPlainText( "Satellite In use" );
563
      }
564
      else
565
      {
566
        mGPSPlainTextEdit->appendPlainText( "Satellite Not in use" );
567
      }
568
#endif
569
#if 0
570
      mGPSPlainTextEdit->appendPlainText( "id: " + QString::number( currentInfo.id ) );
571
      mGPSPlainTextEdit->appendPlainText( "elevation: " + QString::number( currentInfo.elevation ) );
572
      mGPSPlainTextEdit->appendPlainText( "azimuth: " + QString::number( currentInfo.azimuth ) );
573
      mGPSPlainTextEdit->appendPlainText( "signal: " + QString::number( currentInfo.signal ) );
574
#else // as a single line per satellite - easier to read
575
      mGPSPlainTextEdit->appendPlainText( "sat: " + QString::number( currentInfo.id )
576
      + ", elev: " + QString::number( currentInfo.elevation )
577
      + ", azimuth: " + QString::number( currentInfo.azimuth )
578
      + ", signal: " + QString::number( currentInfo.signal ) );
579
#endif
580
    } //debug
581
#endif
582

  
583
    if ( mStackedWidget->currentIndex() == 2 && info.satInfoComplete ) //satellites
419 584
    {
420
      myColour = Qt::black; //strong signal
421
    }
422
    mypMarker->setSymbol( QwtSymbol( QwtSymbol::Ellipse,
423
                                     QBrush( Qt::black ), QPen( myColour ), QSize( 9, 9 ) ) );
424
    mypMarker->setLabelAlignment( Qt::AlignHCenter | Qt::AlignTop );
425
    QwtText text( QString::number( currentInfo.id ) );
426
    text.setColor( myColour );
427
    QColor bg( Qt::white );
428
    bg.setAlpha( 200 );
429
    text.setBackgroundBrush( QBrush( bg ) );
430
    mypMarker->setLabel( text );
431
    mypMarker->attach( mpSatellitesWidget );
432
    mMarkerList << mypMarker;
433
  }
434
  mpCurve->setData( myXData, mySignalData );
435
  mpPlot->replot();
436
  if ( mpMapMarker )
437
    delete mpMapMarker;
585
      QColor bg( Qt::white ); // moved several items outside of the following if block to minimize loop time
586
      bg.setAlpha( 200 );
587
      QColor myColour;
588
      QBrush symbolBrush( Qt::black );
589
      QBrush textBgBrush( bg );
590
      QSize markerSize( 9, 9 );
591
      // Add a marker to the polar plot
592
      if ( currentInfo.id > 0 )       // don't show satellite if id=0 (no satellite indication)
593
      {
594
        QwtPolarMarker *mypMarker = new QwtPolarMarker();
595
        mypMarker->setPosition( QwtPolarPoint( currentInfo.azimuth, currentInfo.elevation ) );
596
        if ( currentInfo.signal < 30 ) //weak signal
597
        {
598
          myColour = Qt::red;
599
        }
600
        else
601
        {
602
          myColour = Qt::black; //strong signal
603
        }
604
        mypMarker->setSymbol( QwtSymbol( QwtSymbol::Ellipse,
605
                                         symbolBrush , QPen( myColour ), markerSize ) );
606
        mypMarker->setLabelAlignment( Qt::AlignHCenter | Qt::AlignTop );
607
        QwtText text( QString::number( currentInfo.id ) );
608
        text.setColor( myColour );
609
        text.setBackgroundBrush( textBgBrush );
610
        mypMarker->setLabel( text );
611
        mypMarker->attach( mpSatellitesWidget );
612
        mMarkerList << mypMarker;
613
      } // currentInfo.id > 0
614
    } //satellites
615
  } //satellite processing loop
616

  
617
  if ( mStackedWidget->currentIndex() == 1 && info.satInfoComplete ) //signal
618
  {
619
    mpCurve->setData( myXData, mySignalData );
620
    mpPlot->replot();
621
  } //signal
622

  
623
  if ( mStackedWidget->currentIndex() == 2 && info.satInfoComplete ) //satellites
624
  {
625
    mpSatellitesWidget->replot();
626
  } //satellites
438 627

  
439
  //after loosing connection, the first gps info sometimes has uninitialized coords
440 628
  QgsPoint myNewCenter;
441
  if ( doubleNear( info.longitude, 0.0 ) && doubleNear( info.latitude, 0.0 ) )
629
  if ( validFlag )
442 630
  {
443
    myNewCenter = mLastGpsPosition;
631
    myNewCenter = QgsPoint( info.longitude, info.latitude );
444 632
  }
445 633
  else
446 634
  {
447
    myNewCenter = QgsPoint( info.longitude, info.latitude );
635
    myNewCenter = mLastGpsPosition;
448 636
  }
449 637

  
450
  if ( mGroupShowMarker->isChecked() )
638
  if ( mStackedWidget->currentIndex() == 0 ) //position
451 639
  {
452
    mpMapMarker = new QgsGpsMarker( mpCanvas );
453
    mpMapMarker->setSize( mSliderMarkerSize->value() );
454
    mpMapMarker->setCenter( myNewCenter );
455
    mpMapMarker->update();
456
  }
457
  else
640
    mTxtLatitude->setText( QString::number( info.latitude, 'f', 8 ) );
641
    mTxtLongitude->setText( QString::number( info.longitude, 'f', 8 ) );
642
    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
643
    if ( mDateTimeFormat.isEmpty() )
644
    {
645
      mTxtDateTime->setText( info.utcDateTime.toString( Qt::TextDate ) );  // default format
646
    }
647
    else
648
    {
649
      mTxtDateTime->setText( info.utcDateTime.toString( mDateTimeFormat ) );  //user specified format string for testing the millisecond part of time
650
    }
651
    mTxtSpeed->setText( tr( "%1 km/h" ).arg( info.speed, 0, 'f', 1 ) );
652
    mTxtDirection->setText( QString::number( info.direction, 'f', 1 ) );
653
    mTxtHdop->setText( QString::number( info.hdop, 'f', 1 ) );
654
    mTxtVdop->setText( QString::number( info.vdop, 'f', 1 ) );
655
    mTxtPdop->setText( QString::number( info.pdop, 'f', 1 ) );
656
    mTxtFixMode->setText( info.fixMode == 'A' ? tr( "Automatic" ) : info.fixMode == 'M' ? tr( "Manual" ) : "" ); // A=automatic 2d/3d, M=manual; allowing for anything else
657
    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
658
    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) : "" ); // allowing for anything else
659
    mTxtSatellitesUsed->setText( QString::number( info.satellitesUsed ) );
660
    mTxtStatus->setText( info.status == 'A' ? tr( "Valid") : info.status == 'V' ? tr( "Invalid" ) : "" );
661
  } //position
662

  
663
#if 0
664
  if ( mStackedWidget->currentIndex() == 4 )    //debug
458 665
  {
459
    mpMapMarker = 0;
460
  }
461
  mSpinLatitude->setValue( info.latitude );
462
  mSpinLongitude->setValue( info.longitude );
463
  mSpinElevation->setValue( info.elevation );
464
  mHorizontalAccuracy->setValue( 10 - info.hdop );
465
  mVerticalAccuracy->setValue( 10 - info.vdop );
466
  mGPSTextEdit->append( "longitude: " + QString::number( info.longitude ) );
467
  mGPSTextEdit->append( "latitude: " + QString::number( info.latitude ) );
468
  mGPSTextEdit->append( "elevation: " + QString::number( info.elevation ) );
469
  mGPSTextEdit->append( "pdop: " + QString::number( info.pdop ) );
470
  mGPSTextEdit->append( "hdop: " + QString::number( info.hdop ) );
471
  mGPSTextEdit->append( "vdop: " + QString::number( info.vdop ) );
666
    QString s;
667
    for ( int i = 0; i < info.satPrn.size(); i++ )
668
    {
669
      if ( info.satPrn.at( i ) > 0 )  //don't show 0 (no satellite)
670
      {
671
        s.append( QString::number( info.satPrn.at( i ) ).append( " " ) );
672
      }
673
    }
674
    mGPSPlainTextEdit->appendPlainText( "UTC date/time: " + info.utcDateTime.toString() );
675
    mGPSPlainTextEdit->appendPlainText( "longitude: " + QString::number( info.longitude, 'f', 8 ) );
676
    mGPSPlainTextEdit->appendPlainText( "latitude: " + QString::number( info.latitude, 'f', 8 ) );
677
    mGPSPlainTextEdit->appendPlainText( "elevation: " + QString::number( info.elevation ) );
678
    mGPSPlainTextEdit->appendPlainText( "speed (knots): " + QString::number( info.speed ) );
679
    mGPSPlainTextEdit->appendPlainText( "direction: " + QString::number( info.direction ) );
680
    mGPSPlainTextEdit->appendPlainText( "pdop: " + QString::number( info.pdop ) );
681
    mGPSPlainTextEdit->appendPlainText( "hdop: " + QString::number( info.hdop ) );
682
    mGPSPlainTextEdit->appendPlainText( "vdop: " + QString::number( info.vdop ) );
683
    mGPSPlainTextEdit->appendPlainText( "satellites in use: " + s );
684
    mGPSPlainTextEdit->appendPlainText( "fix mode: " + QString( info.fixMode ) );
685
    mGPSPlainTextEdit->appendPlainText( "fix type: " + QString::number( info.fixType ) );
686
  } //debug
687
#endif
472 688

  
473 689
  // Avoid refreshing / panning if we havent moved
474 690
  if ( mLastGpsPosition != myNewCenter )
475 691
  {
476 692
    mLastGpsPosition = myNewCenter;
477
    if ( mCbxAutoAddVertices->isChecked() )
478
    {
479
      addVertex();
480
    }
481
    // Pan based on user specified behaviour
482 693

  
694
    // Pan based on user specified behaviour
483 695
    if ( radRecenterMap->isChecked() || radRecenterWhenNeeded->isChecked() )
484 696
    {
485 697
      QgsCoordinateReferenceSystem mypSRS = mpCanvas->mapRenderer()->destinationCrs();
486
      QgsCoordinateReferenceSystem myLatLongRefSys = QgsCoordinateReferenceSystem( 4326 );
487
      QgsCoordinateTransform myTransform( myLatLongRefSys, mypSRS );
698
      QgsCoordinateTransform myTransform( mWgs84CRS, mypSRS ); // use existing WGS84 CRS
488 699

  
489 700
      QgsPoint myPoint = myTransform.transform( myNewCenter );
490 701
      //keep the extent the same just center the map canvas in the display so our feature is in the middle
491
      QgsRectangle myRect(
492
        myPoint.x( ) - ( mpCanvas->extent( ).width( ) / 2 ),
493
        myPoint.y( ) - ( mpCanvas->extent( ).height( ) / 2 ),
494
        myPoint.x( ) + ( mpCanvas->extent( ).width( ) / 2 ),
495
        myPoint.y( ) + ( mpCanvas->extent( ).height( ) / 2 ) );
702
      QgsRectangle myRect( myPoint, myPoint );  // empty rect can be used to set new extent that is centered on the point used to construct the rect
703

  
704
      // testing if position is outside some proportion of the map extent
705
      // this is a user setting - useful range: 5% to 100% (0.05 to 1.0)
706
      QgsRectangle myExtentLimit( mpCanvas->extent() );
707
      myExtentLimit.scale( mSpinMapExtentMultiplier->value() * 0.01 );
496 708

  
497 709
      // only change the extents if the point is beyond the current extents to minimise repaints
498
      if (( !mpCanvas->extent().contains( myPoint ) &&
499
            radRecenterWhenNeeded->isChecked() ) ||
500
          radRecenterMap->isChecked() )
710
      if ( radRecenterMap->isChecked() ||
711
           ( radRecenterWhenNeeded->isChecked() && !myExtentLimit.contains( myPoint ) ) )
501 712
      {
502 713
        mpCanvas->setExtent( myRect );
503 714
        mpCanvas->refresh( );
504 715
      }
505 716
    } //otherwise never recenter automatically
717

  
718
    if ( mCbxAutoAddVertices->isChecked() )
719
    {
720
      addVertex();
721
    }
722
  } // mLastGpsPosition != myNewCenter
723

  
724
  // new marker position after recentering
725
  if ( mGroupShowMarker->isChecked() ) // show marker
726
  {
727
    if ( validFlag ) // update cursor position if valid position
728
    {                // initially, cursor isn't drawn until first valid fix; remains visible until GPS disconnect
729
      if ( ! mpMapMarker )
730
      {
731
        mpMapMarker = new QgsGpsMarker( mpCanvas );
732
      }
733
      mpMapMarker->setSize( mSliderMarkerSize->value() );
734
      mpMapMarker->setCenter( myNewCenter );
735
    }
506 736
  }
737
  else
738
  {
739
    if ( mpMapMarker )
740
    {
741
      delete mpMapMarker;
742
      mpMapMarker = 0;
743
    }
744
  } // show marker
507 745
}
508 746

  
747
#if 0 // not needed - signals/slots set up in UI file; may be needed in the future if GPS valid is used to control whether button is available (should use enable/disable instead of show/hide)
509 748
void QgsGPSInformationWidget::on_mCbxAutoAddVertices_toggled( bool theFlag )
510 749
{
511 750
  if ( theFlag )
......
517 756
    mBtnAddVertex->show();
518 757
  }
519 758
}
759
#endif
520 760

  
521 761
void QgsGPSInformationWidget::on_mBtnAddVertex_clicked( )
522 762
{
......
535 775
  // calling close feature
536 776
  mCaptureList.push_back( mLastGpsPosition );
537 777
  // we store the rubber band points in map canvas CRS so transform to map crs
778
  // potential problem with transform errors and wrong coordinates if map CRS is changed after points are stored - SLM
779
  // should catch map CRS change and transform the points
538 780
  QgsPoint myPoint;
539 781
  if ( mpCanvas && mpCanvas->mapRenderer() )
540 782
  {
......
550 792

  
551 793
void QgsGPSInformationWidget::on_mBtnResetFeature_clicked( )
552 794
{
553
  mNmea->disconnect();
554
  if ( mpRubberBand )
555
  {
556
    delete mpRubberBand;
557
    mpRubberBand = 0;
558
  }
559
  createRubberBand( );
795
  mNmea->disconnect( this, SLOT( displayGPSInformation( const QgsGPSInformation& ) ) );
796
  createRubberBand( ); //deletes existing rubberband
560 797
  mCaptureList.clear();
561 798
  connectGpsSlot();
562 799
}
563 800

  
564 801
void QgsGPSInformationWidget::on_mBtnCloseFeature_clicked( )
565 802
{
566
  mNmea->disconnect();
567 803
  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mpCanvas->currentLayer() );
804
  QGis::WkbType layerWKBType = vlayer->wkbType();
568 805

  
569 806
  // -------------- preconditions ------------------------
807
  // most of these preconditions are already handled due to the button being enabled/disabled based on layer geom type and editing capabilities, but not on valid GPS data
808
#if 0
570 809
  if ( !vlayer )
571 810
  {
572 811
    QMessageBox::information( 0, tr( "Not a vector layer" ),
573 812
                              tr( "The current layer is not a vector layer" ) );
574
    connectGpsSlot();
575 813
    return;
576 814
  }
577 815
  QGis::WkbType layerWKBType = vlayer->wkbType();
578 816

  
579 817
  //no support for adding features to 2.5D types yet
580
  if ( layerWKBType == QGis::WKBLineString25D || layerWKBType == QGis::WKBPolygon25D ||
581
       layerWKBType == QGis::WKBMultiLineString25D || layerWKBType == QGis::WKBPoint25D || layerWKBType == QGis::WKBMultiPoint25D )
818
  if ( layerWKBType == QGis::WKBPoint25D ||
819
       layerWKBType == QGis::WKBLineString25D ||
820
       layerWKBType == QGis::WKBPolygon25D ||
821
       layerWKBType == QGis::WKBMultiPoint25D ||
822
       layerWKBType == QGis::WKBMultiLineString25D ||
823
       layerWKBType == QGis::WKBMultiPolygon25D )
582 824
  {
583 825
    QMessageBox::critical( 0, tr( "2.5D shape type not supported" ), tr(
584 826
                             "Adding features to 2.5D shapetypes is not supported yet. Please "
585 827
                             "select a different editable, non 2.5D layer and try again." ) );
586
    connectGpsSlot();
587 828
    return;
588 829
  }
589 830

  
590 831
  // Multipart features not supported
591
  if ( layerWKBType == QGis::WKBMultiLineString ||
592
       layerWKBType == QGis::WKBMultiPoint ||
832
  if ( layerWKBType == QGis::WKBMultiPoint ||
593 833
       layerWKBType == QGis::WKBMultiLineString ||
594 834
       layerWKBType == QGis::WKBMultiPolygon )
595 835
  {
596 836
    QMessageBox::critical( 0, tr( "Multipart shape type not supported" ), tr(
597 837
                             "Adding features to multipart shapetypes is not supported yet. Please "
598 838
                             "select a different editable, non 2.5D layer and try again." ) );
599
    connectGpsSlot();
600 839
    return;
601 840

  
602 841
  }
......
607 846
  {
608 847
    QMessageBox::information( 0, tr( "Layer cannot be added to" ),
609 848
                              tr( "The data provider for this layer does not support the addition of features." ) );
610
    connectGpsSlot();
611 849
    return;
612 850
  }
613 851

  
......
616 854
    QMessageBox::information( 0, tr( "Layer not editable" ),
617 855
                              tr( "Cannot edit the vector layer. Use 'Toggle Editing' to make it editable." )
618 856
                            );
619
    connectGpsSlot();
620 857
    return;
621 858
  }
859
#endif
622 860

  
623 861
  //lines: bail out if there are not at least two vertices
624 862
  if ( layerWKBType == QGis::WKBLineString  && mCaptureList.size() < 2 )
625 863
  {
626 864
    QMessageBox::information( 0, tr( "Not enough vertices" ),
627 865
                              tr( "Cannot close a line feature until it has at least two vertices." ) );
628
    connectGpsSlot();
629 866
    return;
630 867
  }
631 868

  
......
634 871
  {
635 872
    QMessageBox::information( 0, tr( "Not enough vertices" ),
636 873
                              tr( "Cannot close a polygon feature until it has at least three vertices." ) );
637
    connectGpsSlot();
638 874
    return;
639 875
  }
640 876
  // -------------- end of preconditions ------------------------
......
644 880
  //
645 881
  if ( layerWKBType == QGis::WKBPoint )
646 882
  {
647
    //only do the rest for provider with feature addition support
648
    //note that for the grass provider, this will return false since
649
    //grass provider has its own mechanism of feature addition
650
    if ( provider->capabilities() & QgsVectorDataProvider::AddFeatures )
651
    {
652
      QgsFeature* f = new QgsFeature( 0, "WKBPoint" );
653

  
654
      int size = 0;
655
      char end = QgsApplication::endian();
656
      unsigned char *wkb = NULL;
657
      int wkbtype = 0;
883
    QgsFeature* f = new QgsFeature( 0, "WKBPoint" );
658 884

  
659
      QgsCoordinateTransform t( mWgs84CRS, vlayer->crs() );
660
      QgsPoint myPoint = t.transform( mLastGpsPosition );
661
      double x = myPoint.x();
662
      double y = myPoint.y();
885
    int size = 0;
886
    char end = QgsApplication::endian();
887
    unsigned char *wkb = NULL;
888
    int wkbtype = 0;
663 889

  
664
      size = 1 + sizeof( int ) + 2 * sizeof( double );
665
      wkb = new unsigned char[size];
666
      wkbtype = QGis::WKBPoint;
667
      memcpy( &wkb[0], &end, 1 );
668
      memcpy( &wkb[1], &wkbtype, sizeof( int ) );
669
      memcpy( &wkb[5], &x, sizeof( double ) );
670
      memcpy( &wkb[5] + sizeof( double ), &y, sizeof( double ) );
890
    QgsCoordinateTransform t( mWgs84CRS, vlayer->crs() );
891
    QgsPoint myPoint = t.transform( mLastGpsPosition );
892
    double x = myPoint.x();
893
    double y = myPoint.y();
671 894

  
672
      f->setGeometryAndOwnership( &wkb[0], size );
895
    size = 1 + sizeof( int ) + 2 * sizeof( double );
896
    wkb = new unsigned char[size];
897
    wkbtype = QGis::WKBPoint;
898
    memcpy( &wkb[0], &end, 1 );
899
    memcpy( &wkb[1], &wkbtype, sizeof( int ) );
900
    memcpy( &wkb[5], &x, sizeof( double ) );
901
    memcpy( &wkb[5] + sizeof( double ), &y, sizeof( double ) );
673 902

  
674
      QgsFeatureAction action( tr( "Feature added" ), *f, vlayer, -1, this );
675
      if ( action.addFeature() )
676
        mpCanvas->refresh();
903
    f->setGeometryAndOwnership( &wkb[0], size );
677 904

  
678
      delete f;
905
    QgsFeatureAction action( tr( "Feature added" ), *f, vlayer, -1, this );
906
    if ( action.addFeature() )
907
    {
908
      if ( mCbxAutoCommit->isChecked() )
909
      {
910
        // should canvas->isDrawing() be checked?
911
        if ( !vlayer->commitChanges() ) //assumed to be vector layer and is editable and is in editing mode (preconditions have been tested)
912
        {
913
          QMessageBox::information( this,
914
                                    tr( "Error" ),
915
                                    tr( "Could not commit changes to layer %1\n\nErrors: %2\n" )
916
                                    .arg( vlayer->name() )
917
                                    .arg( vlayer->commitErrors().join( "\n  " ) ) );
918
        }
919

  
920
        vlayer->startEditing();
921
      }
679 922
    }
680
  }
923

  
924
    delete f;
925
  } // layerWKBType == QGis::WKBPoint
681 926
  else // Line or poly
682 927
  {
928
    mNmea->disconnect(this, SLOT( displayGPSInformation( const QgsGPSInformation& ) ) );
929

  
683 930
    //create QgsFeature with wkb representation
684 931
    QgsFeature* f = new QgsFeature( 0, "WKBLineString" );
685 932
    unsigned char* wkb;
......
775 1022
        return;
776 1023
      }
777 1024
    }
778
    else //unknown type
1025
    // Should never get here, as preconditions should have removed any that aren't handled
1026
    else // layerWKBType == QGis::WKBPolygon  -  unknown type
779 1027
    {
780 1028
      QMessageBox::critical( 0, tr( "Error" ), tr( "Cannot add feature. "
781 1029
                             "Unknown WKB type. Choose a different layer and try again." ) );
782 1030
      connectGpsSlot();
783 1031
      return; //unknown wkbtype
784
    }
1032
    } // layerWKBType == QGis::WKBPolygon
785 1033

  
786 1034
    QgsFeatureAction action( tr( "Feature added" ), *f, vlayer, -1, this );
787 1035
    if ( action.addFeature() )
788
      mpCanvas->refresh();
1036
    {
1037
      if ( mCbxAutoCommit->isChecked() )
1038
      {
1039
        if ( !vlayer->commitChanges() ) //swiped... er... appropriated from QgisApp saveEdits()
1040
        {
1041
          QMessageBox::information( this,
1042
                                    tr( "Error" ),
1043
                                    tr( "Could not commit changes to layer %1\n\nErrors: %2\n" )
1044
                                    .arg( vlayer->name() )
1045
                                    .arg( vlayer->commitErrors().join( "\n  " ) ) );
1046
        }
1047

  
1048
        vlayer->startEditing();
1049
      }
1050
      delete mpRubberBand;
1051
      mpRubberBand = NULL;
789 1052

  
790
    delete f;
1053
      // delete the elements of mCaptureList
1054
      mCaptureList.clear();
1055
    } // action.addFeature()
791 1056

  
792
    delete mpRubberBand;
793
    mpRubberBand = NULL;
1057
    delete f;
1058
    connectGpsSlot();
1059
  } // layerWKBType == QGis::WKBPoint
1060
  mpCanvas->refresh();  // NOTE: cancelling feature add refreshes canvas, OK does not; this may change, however, so do it anyway
794 1061

  
795
    // delete the elements of mCaptureList
796
    mCaptureList.clear();
797
    mpCanvas->refresh();
798
  }
799
  connectGpsSlot();
1062
  // force focus back to GPS window/ Add Feature button for ease of use by keyboard
1063
  activateWindow();
1064
  mBtnCloseFeature->setFocus( Qt::OtherFocusReason );
800 1065
}
801 1066

  
802 1067
void QgsGPSInformationWidget::connectGpsSlot( )
......
836 1101
  {
837 1102
    delete mpRubberBand;
838 1103
  }
839
  QSettings settings;
840 1104
  mpRubberBand = new QgsRubberBand( mpCanvas, false );
841
  setTrackColour();
842
  mpRubberBand->setWidth( settings.value( "/gps/trackWidth", 2 ).toInt() );
1105
  mpRubberBand->setColor( mTrackColour );
1106
  mpRubberBand->setWidth( mSpinTrackWidth->value() );
843 1107
  mpRubberBand->show();
844 1108
}
845 1109

  
1110
#if 0 // this function isn't used
846 1111
QPointF QgsGPSInformationWidget::gpsToPixelPosition( const QgsPoint& point )
847 1112
{
848 1113
  //transform to map crs
......
861 1126
  mpCanvas->getCoordinateTransform()->transformInPlace( x, y );
862 1127
  return QPointF( x, y ) ;
863 1128
}
1129
#endif
1130

  
1131
void QgsGPSInformationWidget::on_mBtnLogFile_clicked()
1132
{
1133
//=========================
1134
  // This does not allow for an extension other than ".nmea"
1135
  // Retrieve last used log file dir from persistent settings
1136
  QSettings settings;
1137
  QString settingPath( "/gps/lastLogFileDir" );
1138
  QString lastUsedDir = settings.value( settingPath, "." ).toString();
1139
  QString saveFilePath = QFileDialog::getSaveFileName( this, tr( "Save GPS log file as" ), lastUsedDir, tr( "NMEA files (*.nmea)" ) );
1140
  if ( saveFilePath.isNull() ) //canceled
1141
  {
1142
    return;
1143
  }
1144
  QFileInfo myFI( saveFilePath );
1145
  QString myPath = myFI.path();
1146
  settings.setValue( settingPath, myPath );
1147

  
1148
  // make sure the .nmea extension is included in the path name. if not, add it...
1149
  if ( "nmea" != myFI.suffix() )
1150
  {
1151
    saveFilePath = myFI.filePath() + ".nmea";
1152
  }
1153
  mTxtLogFile->setText( saveFilePath );
1154
  mTxtLogFile->setToolTip( saveFilePath );
1155
}
1156

  
1157
void QgsGPSInformationWidget::logNmeaSentence( const QString& nmeaString )
1158
{
1159
  if ( mLogFileGroupBox->isChecked() && mLogFile && mLogFile->isOpen() )
1160
  {
1161
    mLogFileTextStream << nmeaString << "\r\n"; // specifically output CR + LF (NMEA requirement)
1162
  }
1163
}
1164

  
1165
void QgsGPSInformationWidget::updateCloseFeatureButton( QgsMapLayer * lyr )
1166
{
1167
  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( lyr );
1168

  
1169
  // Add feature button tracks edit state of layer
1170
  if ( vlayer != mpLastLayer )
1171
  {
1172
    if ( mpLastLayer )  // disconnect previous layer
1173
    {
1174
      disconnect( mpLastLayer, SIGNAL( editingStarted() ), this, SLOT( layerEditStateChanged() ) );
1175
      disconnect( mpLastLayer, SIGNAL( editingStopped() ), this, SLOT( layerEditStateChanged() ) );
1176
    }
1177
    if ( vlayer ) // connect new layer
1178
    {
1179
      connect( vlayer, SIGNAL( editingStarted() ), this, SLOT( layerEditStateChanged() ) );
1180
      connect( vlayer, SIGNAL( editingStopped() ), this, SLOT( layerEditStateChanged() ) );
1181
    }
1182
    mpLastLayer = vlayer;
1183
  }
1184

  
1185
  QString buttonLabel = tr( "&Add feature" );
1186
  if ( vlayer ) // must be vector layer
1187
  {
1188
    QgsVectorDataProvider* provider = vlayer->dataProvider();
1189
    QGis::WkbType layerWKBType = vlayer->wkbType();
1190

  
1191
    bool enable =
1192
      ( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) &&  // layer can add features
1193
      vlayer->isEditable() && // layer is editing
1194
      ( // layer has geometry type that can be handled
1195
        layerWKBType == QGis::WKBPoint ||
1196
        layerWKBType == QGis::WKBLineString ||
1197
        layerWKBType == QGis::WKBPolygon
1198
        // add more types here as they are handled
1199
      )
1200
    ;
1201
    switch ( layerWKBType )
1202
    {
1203
      case QGis::WKBPoint:
1204
        buttonLabel = tr( "&Add Point" );
1205
        break;
1206
      case QGis::WKBLineString:
1207
        buttonLabel = tr( "&Add Line" );
1208
        break;
1209
      case QGis::WKBPolygon:
1210
        buttonLabel = tr( "&Add Polygon" );
1211
        break;
1212
      // for the future (also prevent compiler warnings)
1213
      case QGis::WKBMultiPoint:
1214
      case QGis::WKBMultiLineString:
1215
      case QGis::WKBMultiPolygon:
1216
      case QGis::WKBPoint25D:
1217
      case QGis::WKBLineString25D:
1218
      case QGis::WKBPolygon25D:
1219
      case QGis::WKBMultiPoint25D:
1220
      case QGis::WKBMultiLineString25D:
1221
      case QGis::WKBMultiPolygon25D:
1222
      case QGis::WKBUnknown:
1223
      case QGis::WKBNoGeometry:
1224
        ;
1225
    }
1226
    mBtnCloseFeature->setEnabled( enable );
1227
  }
1228
  else
1229
  {
1230
    mBtnCloseFeature->setEnabled( false );
1231
  }
1232
  mBtnCloseFeature->setText( buttonLabel );
1233
}
1234

  
1235
void QgsGPSInformationWidget::layerEditStateChanged()
1236
{
1237
  updateCloseFeatureButton( mpLastLayer );
1238
}
1239

  
1240
void QgsGPSInformationWidget::setStatusIndicator( const FixStatus statusValue )
1241
{
1242
  mLastFixStatus = statusValue;
1243
  // the pixmap will be expanded to the size of the label
1244
  QPixmap status( 4, 4 );
1245
  switch ( statusValue )
1246
  {
1247
    case NoFix:
1248
      status.fill( Qt::red );
1249
      break;
1250
    case Fix2D:
1251
      status.fill( Qt::yellow );
1252
      break;
1253
    case Fix3D:
1254
      status.fill( Qt::green );
1255
      break;
1256
    case NoData:
1257
    default: // anything else - shouldn't happen
1258
      status.fill( Qt::darkGray );
1259
  }
1260
  mLblStatusIndicator->setPixmap( status );
1261
}
1262

  
1263
void QgsGPSInformationWidget::showStatusBarMessage( const QString& msg )
1264
{
1265
  QgisApp::instance()->statusBar()->showMessage( msg );
1266
}
src/app/gps/qgsgpsinformationwidget.h
29 29
class QgsGPSConnection;
30 30
class QgsGPSTrackerThread;
31 31
struct QgsGPSInformation;
32
class QPointF;
32
//class QPointF;
33

  
34
class QgsLegend;
35
class QFile;
36
class QColor;
33 37

  
34 38
/**A dock widget that displays information from a GPS device and
35 39
 * allows the user to capture features using gps readings to
......
44 48
  private slots:
45 49
    void on_mConnectButton_toggled( bool theFlag );
46 50
    void displayGPSInformation( const QgsGPSInformation& info );
47
    void setTrackColour( );
51
    void logNmeaSentence( const QString& nmeaString ); // added to handle 'raw' data
52
    void updateCloseFeatureButton( QgsMapLayer * lyr );
53
    void layerEditStateChanged();
54
 //   void setTrackColour( ); // no longer used
48 55
    void on_mBtnTrackColour_clicked( );
49 56
    void on_mSpinTrackWidth_valueChanged( int theValue );
50 57
    void on_mBtnPosition_clicked( );
......
56 63
    void on_mBtnAddVertex_clicked( );
57 64
    void on_mBtnCloseFeature_clicked( );
58 65
    void on_mBtnResetFeature_clicked( );
59
    void on_mCbxAutoAddVertices_toggled( bool theFlag );
66
// not needed    void on_mCbxAutoAddVertices_toggled( bool theFlag );
67
    void on_mBtnLogFile_clicked();
60 68

  
61 69
    void connected( QgsGPSConnection * );
62 70
    void timedout();
63 71

  
64 72
  private:
73
    enum FixStatus  //GPS status
74
    {
75
      NoData, NoFix, Fix2D, Fix3D
76
    };
65 77
    void addVertex( );
66 78
    void connectGps();
67 79
    void connectGpsSlot( );
68 80
    void disconnectGps();
69 81
    void populateDevices();
82
    void setStatusIndicator( const FixStatus statusValue );
83
    void showStatusBarMessage( const QString& msg );
70 84
    QgsGPSConnection* mNmea;
71 85
    QgsMapCanvas * mpCanvas;
72 86
    QgsGpsMarker * mpMapMarker;
......
76 90
    QList< QwtPolarMarker * > mMarkerList;
77 91
    void createRubberBand( );
78 92
    QgsCoordinateReferenceSystem mWgs84CRS;
79
    QPointF gpsToPixelPosition( const QgsPoint& point );
93
// not used    QPointF gpsToPixelPosition( const QgsPoint& point );
80 94
    QgsRubberBand * mpRubberBand;
81 95
    QgsPoint mLastGpsPosition;
82 96
    QList<QgsPoint> mCaptureList;
97
    FixStatus mLastFixStatus;
98
    QString mDateTimeFormat; // user specified format string in registry (no UI presented)
99
    QgsLegend * mpLegend;
100
    QgsVectorLayer * mpLastLayer;
101
    QFile * mLogFile;
102
    QTextStream mLogFileTextStream;
103
    QColor mTrackColour;
83 104
};
84 105

  
85 106
#endif // QGSGPSINFORMATIONWIDGET_H
src/app/gps/qgsgpsmarker.cpp
14 14
 ***************************************************************************/
15 15

  
16 16
#include <QPainter>
17
#include <QSvgRenderer>
18 17

  
19 18
#include "qgsgpsmarker.h"
20 19
#include "qgscoordinatetransform.h"
......
27 26
{
28 27
  mSize = 16;
29 28
  mWgs84CRS.createFromOgcWmsCrs( "EPSG:4326" );
29
  mSvg.load( QString( ":/images/north_arrows/gpsarrow2.svg" ) );
30
  if ( ! mSvg.isValid() )
31
  {
32
    qDebug( "GPS marker not found!" );
33
  }
30 34
}
31 35

  
32 36
void QgsGpsMarker::setSize( int theSize )
......
60 64

  
61 65
void QgsGpsMarker::paint( QPainter* p )
62 66
{
63
  QSvgRenderer mySVG;
64
  if ( !mySVG.load( QString( ":/images/north_arrows/gpsarrow2.svg" ) ) )
67
  if ( ! mSvg.isValid() )
65 68
  {
66
    qDebug( "GPS marker not found!" );
67 69
    return;
68 70
  }
71

  
72
  // this needs to be done when the canvas is repainted to make for smoother map rendering
73
  // if not done the map could be panned, but the cursor position won't be updated until the next valid GPS fix is received
74
  QPointF pt = toCanvasCoordinates( mCenter );
75
  setPos( pt );
76

  
69 77
  float myHalfSize = mSize / 2.0;
70
  mySVG.render( p, QRectF( 0 - myHalfSize , 0 - myHalfSize, mSize, mSize ) );
78
  mSvg.render( p, QRectF( 0 - myHalfSize , 0 - myHalfSize, mSize, mSize ) );
71 79
}
72 80

  
73 81

  
src/app/gps/qgsgpsmarker.h
19 19
#include "qgsmapcanvasitem.h"
20 20
#include "qgscoordinatereferencesystem.h"
21 21
#include "qgspoint.h"
22
#include <QSvgRenderer>
22 23

  
23 24
class QPainter;
24 25

  
......
50 51

  
51 52
  private:
52 53
    QgsCoordinateReferenceSystem mWgs84CRS;
54
    QSvgRenderer mSvg;
53 55

  
54 56
};
55 57

  
src/app/qgisapp.cpp
452 452
  addDockWidget( Qt::LeftDockWidgetArea, mBrowserWidget );
453 453
  mBrowserWidget->hide();
454 454

  
455
  // create the GPS tool on starting QGIS - this is like the Browser
456
  mpGpsWidget = new QgsGPSInformationWidget( mMapCanvas );
457
  //create the dock widget
458
  mpGpsDock = new QDockWidget( tr( "GPS Information" ), this );
459
  mpGpsDock->setObjectName( "GPSInformation" );
460
  mpGpsDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea );
461
  addDockWidget( Qt::LeftDockWidgetArea, mpGpsDock );
462
  // add to the Panel submenu
463
  // now add our widget to the dock - ownership of the widget is passed to the dock
464
  mpGpsDock->setWidget( mpGpsWidget );
465
  mpGpsDock->hide();
466

  
455 467
  mInternalClipboard = new QgsClipboard; // create clipboard
456 468
  mQgisInterface = new QgisAppInterface( this ); // create the interfce
457 469

  
......
630 642

  
631 643
  delete mPythonUtils;
632 644

  
645
  delete mpGpsWidget;
646

  
633 647
  deletePrintComposers();
634 648
  removeAnnotationItems();
635 649

  
......
729 743
  {
730 744
    showTileScale();
731 745
  }
732
  // Restore state of GPS Tracker
733
  if ( settings.value( "/gps/widgetEnabled", false ).toBool() )
734
  {
735
    showGpsTool();
736
  }
737 746
}
738 747

  
739 748

  
......
830 839
  connect( mActionSetLayerCRS, SIGNAL( triggered() ), this, SLOT( setLayerCRS() ) );
831 840
  connect( mActionSetProjectCRSFromLayer, SIGNAL( triggered() ), this, SLOT( setProjectCRSFromLayer() ) );
832 841
  connect( mActionTileScale, SIGNAL( triggered() ), this, SLOT( showTileScale() ) );
833
  connect( mActionGpsTool, SIGNAL( triggered() ), this, SLOT( showGpsTool() ) );
834 842
  connect( mActionLayerProperties, SIGNAL( triggered() ), this, SLOT( layerProperties() ) );
835 843
  connect( mActionLayerSubsetString, SIGNAL( triggered() ), this, SLOT( layerSubsetString() ) );
836 844
  connect( mActionAddToOverview, SIGNAL( triggered() ), this, SLOT( isInOverview() ) );
......
1871 1879
    settings.setValue( "/UI/tileScaleEnabled", false );
1872 1880
  }
1873 1881

  
1874
  // Persist state of GPS Tracker
1875
  if ( mpGpsWidget )
1876
  {
1877
    settings.setValue( "/gps/widgetEnabled", true );
1878
    delete mpGpsWidget;
1879
  }
1880
  else
1881
  {
1882
    settings.setValue( "/gps/widgetEnabled", false );
1883
  }
1884

  
1885 1882
  QgsPluginRegistry::instance()->unloadAll();
1886 1883
}
1887 1884

  
......
4518 4515
  mMapCanvas->refresh();
4519 4516
}
4520 4517

  
4521
void QgisApp::showGpsTool()
4522
{
4523
  if ( !mpGpsWidget )
4524
  {
4525
    mpGpsWidget = new QgsGPSInformationWidget( mMapCanvas );
4526
    //create the dock widget
4527
    mpGpsDock = new QDockWidget( tr( "GPS Information" ), this );
4528
    mpGpsDock->setObjectName( "GPSInformation" );
4529
    mpGpsDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea );
4530
    addDockWidget( Qt::LeftDockWidgetArea, mpGpsDock );
4531
    // add to the Panel submenu
4532
    mPanelMenu->addAction( mpGpsDock->toggleViewAction() );
4533
    // now add our widget to the dock - ownership of the widget is passed to the dock
4534
    mpGpsDock->setWidget( mpGpsWidget );
4535
    mpGpsWidget->show();
4536
  }
4537
  else
4538
  {
4539
    mpGpsDock->setVisible( mpGpsDock->isHidden() );
4540
  }
4541
}
4542

  
4543 4518
void QgisApp::showTileScale()
4544 4519
{
4545 4520
  if ( !mpTileScaleWidget )
src/app/qgisapp.h
265 265
    QAction *actionSetLayerCRS() { return mActionSetLayerCRS; }
266 266
    QAction *actionSetProjectCRSFromLayer() { return mActionSetProjectCRSFromLayer; }
267 267
    QAction *actionTileScale() { return mActionTileScale; }
268
    QAction *actionGpsTool() { return mActionGpsTool; }
269 268
    QAction *actionLayerProperties() { return mActionLayerProperties; }
270 269
    QAction *actionLayerSubsetString() { return mActionLayerSubsetString; }
271 270
    QAction *actionAddToOverview() { return mActionAddToOverview; }
......
483 482
    void setLayerCRS();
484 483
    //! Assign layer CRS to project
485 484
    void setProjectCRSFromLayer();
486
    //! Show GPS tool
487
    void showGpsTool();
488 485
    //! Show tile scale slider
489 486
    void showTileScale();
490 487
    //! zoom to extent of layer
src/core/gps/nmeatime.h
32 32
    int     hour;       /**< Hours since midnight - [0,23] */
33 33
    int     min;        /**< Minutes after the hour - [0,59] */
34 34
    int     sec;        /**< Seconds after the minute - [0,59] */
35
    int     hsec;       /**< Hundredth part of second - [0,99] */
35
    int     msec;       /**< Thousandths part of second - [0,999] */
36 36

  
37 37
  } nmeaTIME;
38 38

  
src/core/gps/parse.c
75 75
                                 ) );
76 76
      break;
77 77
    case sizeof( "hhmmss.s" ) - 1:
78
      success = ( 4 == nmea_scanf( buff, buff_sz,
79
                                   "%2d%2d%2d.%d", &( res->hour ), &( res->min ), &( res->sec ), &( res->msec )
80
                                 ) );
81
      res->msec = res->msec * 100;   // tenths sec * 100 = thousandths
82
      break;
78 83
    case sizeof( "hhmmss.ss" ) - 1:
84
      success = ( 4 == nmea_scanf( buff, buff_sz,
85
                                   "%2d%2d%2d.%d", &( res->hour ), &( res->min ), &( res->sec ), &( res->msec )
86
                                 ) );
87
      res->msec = res->msec * 10;   // hundredths sec * 10 = thousandths
88
      break;
79 89
    case sizeof( "hhmmss.sss" ) - 1:
80 90
      success = ( 4 == nmea_scanf( buff, buff_sz,
81
                                   "%2d%2d%2d.%d", &( res->hour ), &( res->min ), &( res->sec ), &( res->hsec )
91
                                   "%2d%2d%2d.%d", &( res->hour ), &( res->min ), &( res->sec ), &( res->msec )
82 92
                                 ) );
93
      // already thousandths
83 94
      break;
84 95
    default:
85 96
      nmea_error( "Parse of time error (format error)!" );
......
380 391
  info->utc.hour = pack->utc.hour;
381 392
  info->utc.min = pack->utc.min;
382 393
  info->utc.sec = pack->utc.sec;
383
  info->utc.hsec = pack->utc.hsec;
394
  info->utc.msec = pack->utc.msec;
384 395
  info->sig = pack->sig;
385 396
  info->HDOP = pack->HDOP;
386 397
  info->elv = pack->elv;
src/core/gps/qextserialport/qextserialenumerator.h
13 13

  
14 14
#ifdef Q_OS_WIN
15 15
    #ifdef __MINGW32__
16
      #ifdef _WIN32_WINNT
17
        #if _WIN32_WINNT < 0x0500
18
          #undef _WIN32_WINNT
19
          #define _WIN32_WINNT 0x0500
20
        #endif
21
      #else
... This diff was truncated because it exceeds the maximum size that can be displayed.