Adding GPS data to your ArcGIS Runtime API for Java App

3641
0
05-04-2021 03:40 AM
Labels (1)
MarkBaird
Esri Regular Contributor
2 0 3,641

Although we often associate field data collection activities with mobile phones, it is still very common for these data collection exercises to be carried out using laptop computers.  Being able to view your data on a larger screen with a more feature rich user interface is a common requirement for more complex use cases. 

When working with these types of application, it of course raises the question "how do I get my location?”.  Most laptops do not natively have location devices such as GPS: this is where USB based GPS devices will help.

MarkBaird_0-1620121189433.png

In the background, these USB devices output serial streams of data known as NMEA (National Maritime Electronics Association) sentences.  These sentences contain position, velocity and time information which can be decoded and used to show location and direction on a map application.  The text below shows an example of some NMEA data from a GPS:

$GPGSV,3,3,12,09,12,255,,20,12,068,,23,08,070,,10,03,099,*71
$GPRMC,185842.918,V,,,,,,,010321,,,N*4E
$GPGGA,185843.918,,,,,0,00,,,M,0.0,M,,0000*55
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,185843.918,V,,,,,,,010321,,,N*4F
$GPGGA,185844.915,,,,,0,00,,,M,0.0,M,,0000*5F
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,185844.915,V,,,,,,,010321,,,N*45

Once you are listening into this stream of data coming from your GPS device, we’ve made it easy for you to parse the sentences and turn them into a location which shows on your map.

MarkBaird_1-1620121383280.png

The full code for my application is in this git repository if you just wanted to pull it and try it out.  I have however given an explanation of how it works so you can use this example effectively in your own app.

I’ve left plenty of console output messages within the code so you can get a feel for what is happening, but I appreciate you will delete all this once you’ve see it working successfully.

Starting from the very basics our app has a MapView class which is a JavaFX control which displays our Map.  Next you will see I’ve created a NmeaLocationDataSource which is the class which will process the strings of data coming from the GPS device.  This in turn is used to set the LocationDisplay on the map which is what draws the location marker for you.

 

 

 

// make location data source and link to Location Display
nmeaLocationDataSource = new NmeaLocationDataSource();
mapView.getLocationDisplay().setLocationDataSource(nmeaLocationDataSource);

// start location data source and wait for it to be ready
nmeaLocationDataSource.startAsync();
nmeaLocationDataSource.addStartedListener(()-> {
    gpsReader = new GPSReader(nmeaLocationDataSource);
});

 

 

 

Looks simple so far, but this is where the fun begins when we work out how to read the stream of data from the GPS device.  As this is a Java application it could be working on Windows, Linux or Mac so for it to be properly functional as a cross platform application we need to choose our serial port reading techniques carefully.  Unfortunately, the core Java libraries are not well equipped for the dark art of reading from serial ports (RS232 basically)  but I did find a nice library which works for just this task and works cross platform too.  Step forward the jSerialComm library!  It’s pretty reliable and I’ve had it working successfully on my Mac and Windows machines.

The jSerialComm library gives you the ability to list serial ports and for any serial port you can connect to, it will read byte streams from the port.   In my code I start by going through each of the available ports to see if I can find a GPS device:

 

 

 

private void startGPS() {
    // try each available port in turn
    for (SerialPort serialPort : SerialPort.getCommPorts()) {
        System.out.println("detected port name " + serialPort.getSystemPortName());
        PortChecker portChecker = new PortChecker(serialPort);
        portChecker.start();
    }
}

 

 

 

When connecting to a port, you need to specify the serial port communication parameters such as the baud rate, data bits and stop bits.  The challenge here is getting the parameters right for the device.  For the majority of GPS devices, you can usually get away with:

  • Baud rate: 4800
  • Data bits: 8
  • Stop bits: 1
  • Parity: 0

However, I’ve come across devices which use a different baud rate.  If you get the baud rate wrong you can still connect to the port and read the byte stream, but the data you get out won’t be of much use.  This is what I get when reading on my device with the wrong baud rate:

��������������x���`x`�~��f�x�x�xx�xx�~xx�x`x�x�x�x�x�`xx�x~x�xx�x�x�x�x`x�xx�x�xx�x�xx�x���~x�x��`x`�~��f�x�x�xx�xx�fxx�xx��x�x�`xx�xxx��xx�x�x�x�x�x��xx�xx�x�xx��~x`怘�`x`�~��f�x�x�xx�x�x�x�xxx��xx�xx�xx�x��x�xx�xx`x��x�xx�x�x���~x����`x`����xx�x�xx�x�xxx�x��������x�xxxx�x����`xx��`x`�~�`�xx�x�xx�x�xxx�����x�xx�`x�x����x�x����xxxxf~xx怘

You could blindly pass this data to the nmeaLocationDataSource, but you are unlikely to get any locations out of it!

The technique I’ve used here to get the correct baud rate is to connect to each serial port and simply try out different baud rates to see if I get a valid NMEA sentence.  If you can read the characters “$GP” at the start of your sentence you’ve got the correct baud rate.  If you’ve been reading the port for a second and you get nothing or no sign of a “$GP” you can assume you’ve got it wrong and try another baud rate.  I’ve not detailed the code in this blog, but this should give you enough information to understand how it works

Once you’ve found a GPS device and you are listening into the stream of bytes you just need pass the bytes of data to the LocationDataSource and it will update your location display on the map:

 

 

 

// send the data to the location data source for parsing
nmeaLocationDataSource.pushData(newData);

 

 

 

And that’s it, as long as you are processing that NMEA data you’ll get a constantly updated location display which can be used for collecting new data or showing your position for routing for example.  All this will be happening on a background thread so it will not be blocking your UI thread so your application will always remain responsive.

Of course, when you do shut down your application you will need to close the connection to the USB port you have opened otherwise the background thread will not stop reading your GPS data and your application will not fully terminate.  See the close method on the GpsReader class.

 

 

 

/**
 * Stops and releases all resources used in application.
 */
@Override
public void stop() {
    if (gpsReader != null) gpsReader.close();
    if (mapView != null) mapView.dispose();
}

 

 

 

If this code which seeks out a serial port for a USB GPS device is of interest to lots of developers, I will consider adding this capability to our toolkit.  Let me know this this would be of benefit to you.