Parse NMEA sentences and use the results to show device location on the map.

Use case
NMEA sentences can be retrieved from GPS receivers and parsed into a series of coordinates with additional information. Devices without a built-in GPS receiver can retrieve NMEA sentences by using a separate GPS dongle, commonly connected bluetooth or through a serial port.
The NMEA location data source allows for detailed interrogation of the information coming from the GPS receiver. For example, allowing you to report the number of satellites in view.
How to use the sample
Tap “Start” to parse the NMEA sentences into a simulated location data source, and initiate the location display. Tap “Reset” to reset the location display.
How it works
- Load an NMEA string and parse the sentences into a series of locations.
- Create an
NmeaLocationDataSourceand initialize it with the mock locations. - Set it to the location display’s data source.
- Start the location display to begin receiving location and satellite updates.
Relevant API
- Location
- LocationDisplay
- NmeaLocationDataSource
About the data
This sample reads lines from a local file to simulate the feed of data into the NmeaLocationDataSource. This simulated data source provides NMEA data periodically, and allows the sample to be used on devices without a GPS dongle that produces NMEA data.
The route taken in this sample features a two-minute driving trip around Redlands, CA.
Tags
dongle, GPS, history, navigation, NMEA, real-time, trace
Sample Code
// [WriteFile Name=DisplayDeviceLocationWithNmeaDataSources, Category=Maps]// [Legal]// Copyright 2021 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifdef PCH_BUILD#include "pch.hpp"#endif // PCH_BUILD
// sample headers#include "DisplayDeviceLocationWithNmeaDataSources.h"
// ArcGIS Maps SDK headers#include "LocationDisplay.h"#include "Map.h"#include "MapQuickView.h"#include "MapTypes.h"#include "MapViewTypes.h"#include "NmeaLocationDataSource.h"#include "Point.h"#include "SpatialReference.h"
// Qt headers#include <QFile>#include <QFuture>#include <QTimer>
using namespace Esri::ArcGISRuntime;
DisplayDeviceLocationWithNmeaDataSources::DisplayDeviceLocationWithNmeaDataSources(QObject* parent /* = nullptr */): QObject(parent), m_map(new Map(BasemapStyle::ArcGISNavigation, this)), m_timer(new QTimer(this)){}
DisplayDeviceLocationWithNmeaDataSources::~DisplayDeviceLocationWithNmeaDataSources() = default;
void DisplayDeviceLocationWithNmeaDataSources::init(){ // Register the map view for QML qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView"); qmlRegisterType<DisplayDeviceLocationWithNmeaDataSources>("Esri.Samples", 1, 0, "DisplayDeviceLocationWithNmeaDataSourcesSample");}
MapQuickView* DisplayDeviceLocationWithNmeaDataSources::mapView() const{ return m_mapView;}
// Set the view (created in QML)void DisplayDeviceLocationWithNmeaDataSources::setMapView(MapQuickView* mapView){ if (!mapView || mapView == m_mapView) return;
m_mapView = mapView; m_mapView->setMap(m_map);
m_mapView->setViewpointCenterAsync(Point(-117.191, 34.0306, SpatialReference::wgs84()), 100'000);
// Create a new NMEA location data source m_nmeaLocationDataSource = new NmeaLocationDataSource(SpatialReference::wgs84(), this); m_mapView->locationDisplay()->setDataSource(m_nmeaLocationDataSource); m_mapView->locationDisplay()->setAutoPanMode(LocationDisplayAutoPanMode::Recenter);
emit mapViewChanged();}
void DisplayDeviceLocationWithNmeaDataSources::start(){ // Enable receiving NMEA location data from external device m_nmeaLocationDataSource->start();
// Display the user's location m_mapView->locationDisplay()->start();
// Load simulated NMEA sentences for sample if (m_mockNmeaSentences.isEmpty()) { const QString filePath = ":/Samples/Maps/DisplayDeviceLocationWithNmeaDataSources/redlands.nmea"; if(!loadMockDataFile(filePath)) { qDebug() << "Unable to load file at path:" << filePath; m_nmeaSimulationActive = false; emit nmeaSimulationActiveChanged();
return; } }
// Simulate pushing data to the NMEA location data source connect(m_timer, &QTimer::timeout, this, [this]() { const QByteArray line = m_mockNmeaSentences.at(m_mockDataIterator);
// In a non-simulated scenario, incoming NMEA sentences from a serial port or bluetooth device would be pushed to the location data source in real time // NMEA sentences can be pushed individually or in multiple lines separated by line breaks // Sentences pass information such as direction, velocity, and location and are grouped together to provide detailed information about a user's position m_nmeaLocationDataSource->pushData(line);
++m_mockDataIterator; if (m_mockDataIterator >= m_mockNmeaSentences.size()) m_mockDataIterator = 0; });
m_mockDataIterator = 0; m_timer->start(1000);}
void DisplayDeviceLocationWithNmeaDataSources::reset(){ // Disable simulated data m_timer->stop(); disconnect(m_timer, &QTimer::timeout, nullptr, nullptr);
// Stop displaying user's location m_mapView->locationDisplay()->stop();
// Stop receiving location data m_nmeaLocationDataSource->stop();}
bool DisplayDeviceLocationWithNmeaDataSources::loadMockDataFile(const QString& filePath){
// Load simulated NMEA sentences to display for sample QFile mockDataFile(filePath);
if(!mockDataFile.exists()) return false;
mockDataFile.open(QIODevice::ReadOnly);
while (!mockDataFile.atEnd()) { QByteArray line = mockDataFile.readLine();
// In this simulated data stream, blocks of NMEA sentences start with $GPGGA (which provides the device's position) if (line.startsWith("$GPGGA")) m_mockNmeaSentences.append(line);
// Additional sentences that provide information such as direction and velocity follow and are separated by line breaks else m_mockNmeaSentences.last() += line; }
mockDataFile.close(); return true;}// [WriteFile Name=DisplayDeviceLocationWithNmeaDataSources, Category=Maps]// [Legal]// Copyright 2021 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifndef DISPLAYDEVICELOCATIONWITHNMEADATASOURCES_H#define DISPLAYDEVICELOCATIONWITHNMEADATASOURCES_H
// Qt headers#include <QByteArray>#include <QFile>#include <QObject>#include <QTimer>
namespace Esri::ArcGISRuntime{class Map;class MapQuickView;class NmeaLocationDataSource;}
Q_MOC_INCLUDE("MapQuickView.h")
class DisplayDeviceLocationWithNmeaDataSources : public QObject{ Q_OBJECT
Q_PROPERTY(Esri::ArcGISRuntime::MapQuickView* mapView READ mapView WRITE setMapView NOTIFY mapViewChanged) Q_PROPERTY(bool nmeaSimulationActive MEMBER m_nmeaSimulationActive NOTIFY nmeaSimulationActiveChanged)
public: explicit DisplayDeviceLocationWithNmeaDataSources(QObject* parent = nullptr); ~DisplayDeviceLocationWithNmeaDataSources();
static void init();
Q_INVOKABLE void start(); Q_INVOKABLE void reset();
signals: void mapViewChanged(); void nmeaSimulationActiveChanged();
private: Esri::ArcGISRuntime::MapQuickView* mapView() const; void setMapView(Esri::ArcGISRuntime::MapQuickView* mapView); bool loadMockDataFile(const QString& filePath);
Esri::ArcGISRuntime::Map* m_map = nullptr; Esri::ArcGISRuntime::MapQuickView* m_mapView = nullptr; Esri::ArcGISRuntime::NmeaLocationDataSource* m_nmeaLocationDataSource = nullptr; QList<QByteArray> m_mockNmeaSentences; int m_mockDataIterator; QTimer* m_timer = nullptr; bool m_nmeaSimulationActive = false;};
#endif // DISPLAYDEVICELOCATIONWITHNMEADATASOURCES_H// [WriteFile Name=DisplayDeviceLocationWithNmeaDataSources, Category=Maps]// [Legal]// Copyright 2021 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
import QtQuickimport QtQuick.Controlsimport Esri.Samples
Item {
// add a mapView component MapView { id: view anchors.fill: parent
Component.onCompleted: { // Set the focus on MapView to initially enable keyboard navigation forceActiveFocus(); } }
// Declare the C++ instance which creates the map etc. and supply the view DisplayDeviceLocationWithNmeaDataSourcesSample { id: model mapView: view }
Button { id: button anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom margins: 25 } width: 200 text: model.nmeaSimulationActive ? "RESET" : "START" onClicked: { model.nmeaSimulationActive = !model.nmeaSimulationActive; if (model.nmeaSimulationActive) model.start(); else model.reset(); } }}// [Legal]// Copyright 2021 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
// sample headers#include "DisplayDeviceLocationWithNmeaDataSources.h"
// ArcGIS Maps SDK headers#include "ArcGISRuntimeEnvironment.h"
// Qt headers#include <QDir>#include <QGuiApplication>#include <QQmlApplicationEngine>
// Platform specific headers#ifdef Q_OS_WIN#include <Windows.h>#endif
int main(int argc, char *argv[]){ Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setUseLegacyAuthentication(false); QGuiApplication app(argc, argv); app.setApplicationName(QString("DisplayDeviceLocationWithNmeaDataSources"));
// Use of ArcGIS location services, such as basemap styles, geocoding, and routing services, // requires an access token. For more information see // https://links.esri.com/arcgis-runtime-security-auth.
// The following methods grant an access token:
// 1. User authentication: Grants a temporary access token associated with a user's ArcGIS account. // To generate a token, a user logs in to the app with an ArcGIS account that is part of an // organization in ArcGIS Online or ArcGIS Enterprise.
// 2. API key authentication: Get a long-lived access token that gives your application access to // ArcGIS location services. Go to the tutorial at https://links.esri.com/create-an-api-key. // Copy the API Key access token.
const QString accessToken = QString("");
if (accessToken.isEmpty()) { qWarning() << "Use of ArcGIS location services, such as the basemap styles service, requires" << "you to authenticate with an ArcGIS account or set the API Key property."; } else { Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setApiKey(accessToken); }
// Initialize the sample DisplayDeviceLocationWithNmeaDataSources::init();
// Initialize application view QQmlApplicationEngine engine; // Add the import Path engine.addImportPath(QDir(QCoreApplication::applicationDirPath()).filePath("qml"));
#ifdef ARCGIS_RUNTIME_IMPORT_PATH_2 engine.addImportPath(ARCGIS_RUNTIME_IMPORT_PATH_2);#endif
// Set the source engine.load(QUrl("qrc:/Samples/Maps/DisplayDeviceLocationWithNmeaDataSources/main.qml"));
return app.exec();}// Copyright 2021 Esri.
// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.
import QtQuick.Controlsimport Esri.Samples
ApplicationWindow { visible: true width: 800 height: 600
DisplayDeviceLocationWithNmeaDataSources { anchors.fill: parent }}