Location, Location, Location
Mapping Out
The objective is to show a map of the GPS device's location, and, as it moves, have the map move with it (see Figure 4). A circle indicates where the device is located on the map.

Your first reaction would probably be to base your app on some open source map app that already exists. Marble [4] seems like a good candidate, and if you run it like so
marble --latlon <lat>,<lon> --distance 2 --map "earth/openstreetmap/openstreetmap.dgml"
where lat
and lon
are the latitude and longitude, Marble will open at the desired location, which is already promising.
Then, if you think that
gpspipe -w | sed -n s/.*lat:(.*),/1/p | cut -d, -f1
will give you the latitude of your phone, and
gpspipe -w | sed -n s/.*lon:(.*),/1/p | cut -d, -f1
will give you the longitude, things definitely seem to be shaping up.
The preceding instructions will give you an ongoing stream of latitudes and longitudes. It would be possible to pipe the output to a file and then run through the rows and use them with Marble somehow. But this is an inelegant and convoluted way of doing things. It is much smarter to break out QML [5].
QML App
QML is a relatively simple declarative language that uses the Qt libraries to build graphical applications. How QML works is a bit like how you'd go about programming an interface using a visual IDE, but with text: You "design" the layout describing it in text form, and then you do the same with all the items contained in the layout. To execute actions (say, when a button is pressed), you associate events with JavaScript modules to each object.
Getting back to the original idea, the Marble documentation says that it is easy to develop apps based on the Marble code in QML. It refers the would-be programmer to a tutorial and examples available at [6]. Unfortunately, the code examples and snippets are outdated, and it is quite hard to figure out how to do things. According to one developer, the problem is that Marble's code is in continuous flux, changing nearly daily.
Instead, he suggests using QML's own Location library. To get started, install all the bits and pieces you'll need:
apt install qmlscene qml-module-qtpositioning qml-module-qtlocation
The modules will install positioning and location libraries for your app and drag in all the Qt QML common files. The qmlscene
utility is what you use to run QML apps.
The code for your app might look like Listing 2.
Listing 2
map.qml
01 import QtQuick 2.1 02 import QtQuick.Window 2.0 03 import QtPositioning 5.5 04 import QtLocation 5.6 05 06 Window { 07 id:page 08 width: 800 09 height: 800 10 visible: true 11 12 Map { 13 id:myMap 14 anchors.fill: parent 15 plugin: mapPlugin 16 zoomLevel: 17 17 18 property MapCircle circle 19 20 function update(pos) { 21 removeMapItem(circle); 22 23 circle = Qt.createQmlObject('import QtLocation 5.3; MapCircle {}', page); 24 circle.radius = 30; 25 circle.color = "transparent"; 26 circle.border.color = "red" 27 circle.border.width = 3; 28 myMap.addMapItem(circle); 29 30 circle.center = pos.coordinate; 31 myMap.center = pos.coordinate; 32 33 //console.log("Coordinates: ", pos.coordinate.latitude, pos.coordinate.longitude); 34 } 35 } 36 37 Plugin { 38 id: mapPlugin 39 name: "osm" 40 } 41 42 PositionSource { 43 id: gpsPos 44 updateInterval: 500 45 active: true 46 nmeaSource: "socket://localhost:29999" 47 48 onPositionChanged: { 49 myMap.update(position); 50 } 51 } 52 }
After loading in all the modules you need (lines 1-4), you create an all-encompassing Window
object (line 6). You give it a name (page
), set its size, and make it visible.
Next you create a Map
widget. Again, you give it a name (myMap
), you set its size to automatically fit in Window
(the parent
object), and you set the plugin it will use. In this case, the plugin is declared on lines 37 to 40, where you can see you will be using the OpenStreetMap [7] data for your maps. Finally, you set the zoom level of the map, in this case, 17 meters up in the air.
Map
objects allow you to draw shapes on the map. If you want a circle, you create a circle
object on line 18. You then manipulate it using a JavaScript function (lines 20-34) and moving it (line 30) when the GPS position changes. You also move the view of the map so that the circle is always in the center (line 31) .
Line 33 is for debugging and testing only. Uncomment it to see coordinates scroll by on the terminal window as you move around.
As mentioned previously, QML is declarative and event-based and you can see how that works in the PositionsSource
object (lines 42-49). This is an object provided by the QtPositioning
module, and it is in charge of retrieving and processing the GPS information.
As usual, you give your object a name (gpsPos
) to refer to it later, you set how often it will poll the GPS source (once every 500 milliseconds), and you set from where it will get the NMEA information.
A slight detour here, because it turns out that, unless you have GPS on the laptop or a GPS-only device connected via USB (that is, not a phone), PositionSource
does a poor job of determining where it should get its data.
However, you can force it to look for data by setting the nmeaSource
attribute. You would usually nmeaSource
to point to a file with static NMEA data to "simulate" a GPS positioning (nmeaSource
is primarily used when testing), but you can also point to a server. This got me thinking: First I tried to pipe the output from gpspipe
to a file and read from that:
gpspipe -r > gps.dat
Line 46 then looked like this:
nmeaSource: "gps.dat"
But then PositionSource
was never updated – it just took the last value from the file and never went back to check if there were any more.
A bit more research revealed that PositionSource
can also be read from a socket URL. And know what makes sockets super easy? Netcat [8] does. Netcat (nc
) is preinstalled in nearly every Linux installation on the planet, so it was simply a matter of doing this:
gpspipe -r | nc -l 29999
Then, create a stream of GPS data on localhost
using port 29999
, and get PositionSource
to read from it, as shown on line 46.
Finally, you set up an event that fires when the position you are polling changes (line 48). When it does fire, you visit the update(pos)
function in the Map
object (lines 20-34) and redraw the circle and the map with the new position data, pos.coordinate
, that you pass in the function call.
To summarize, here's what you do to make everything work together:
1. Run gpsd udp://*.29998
.
2. Connect your phone's GPS client to your laptop using your laptop's IP and port 29998.
3. Run gpsmon
to check you are receiving GPS data correctly.
4. Run gpspipe -r | nc -l 29999
to set up a data stream through port 29999 on your computer.
5. Run your application with qmlscene map.qml
.
Conclusion
There you have it: A full GPS app on a laptop (or Raspberry Pi) in 50 lines of code [9]. This is definitely a fun project, and it combines hardware you probably already have hanging around at home in an interesting way. Expanding to build an actually useful application should be a piece of cake.
Famous last words.
Infos
- gpsd: http://www.catb.org/gpsd/
- gpsd client on Google Play: https://play.google.com/store/apps/details?id=io.github.tiagoshibata.gpsdclient
- A (free) gpsd client for Android: https://github.com/tiagoshibata/Android-GPSd-Client
- Marble: https://marble.kde.org/
- QML: https://doc.qt.io/qt-5.10/qtqml-index.html
- Marble for developers: https://marble.kde.org/dev-intro.php
- OpenStreetMap project: https://www.openstreetmap.org/
- Netcat: http://nc110.sourceforge.net/
- Video of the app in action: https://youtu.be/jcHOY07Rvpk
« Previous 1 2
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you've found an article to be beneficial.
News
-
elementary OS 7.1 Now Available for Download
The team behind elementary OS has released the latest version of its operating system with a focus on personalization, inclusivity, accessibility, and privacy.
-
The GNU Project Celebrates Its 40th Birthday
September 27 marks the 40th anniversary of the GNU Project, and it was celebrated with a hacker meeting in Biel/Bienne, Switzerland.
-
Linux Kernel Reducing Long-Term Support
LTS support for the Linux kernel is about to undergo some serious changes that will have a considerable impact on the future.
-
Fedora 39 Beta Now Available for Testing
For fans and users of Fedora Linux, the first beta of release 39 is now available, which is a minor upgrade but does include GNOME 45.
-
Fedora Linux 40 to Drop X11 for KDE Plasma
When Fedora 40 arrives in 2024, there will be a few big changes coming, especially for the KDE Plasma option.
-
Real-Time Ubuntu Available in AWS Marketplace
Anyone looking for a Linux distribution for real-time processing could do a whole lot worse than Real-Time Ubuntu.
-
KSMBD Finally Reaches a Stable State
For those who've been looking forward to the first release of KSMBD, after two years it's no longer considered experimental.
-
Nitrux 3.0.0 Has Been Released
The latest version of Nitrux brings plenty of innovation and fresh apps to the table.
-
Linux From Scratch 12.0 Now Available
If you're looking to roll your own Linux distribution, the latest version of Linux From Scratch is now available with plenty of updates.
-
Linux Kernel 6.5 Has Been Released
The newest Linux kernel, version 6.5, now includes initial support for two very exciting features.