Hello! This article is a continuation of a series of articles devoted to the development of applications for the Sailfish OS mobile platform. This time it will be about how you can implement in the application obtaining information about the geographic location of the device, displaying a map with the current location and the route traveled.
GPS tracker application
To demonstrate the capabilities of the geolocation API, we will use the GPS tracker application, which includes all the basic geolocation-related functions:
- View maps of the area on the phone screen.
- Displays the distance traveled on the map.
- Displays recorded and downloaded GPS tracks from the device’s memory.
- Maintaining a list of points of interest with the ability to enable / disable their display on the map.
- Move the map to the selected point of interest.
Geographic Data Tools
The Sailfish OS platform uses the standard Qt framework classes to provide applications with access to geolocation information. In turn, the Qt framework provides two modules for working with data:
Qt Positioning and
Qt Location . The first subsystem allows you to get information about the current location of the device, and the second provides the means for geocoding and displaying this information on a map. In this article, we will consider only the use of QML types (without C ++ classes), since this is usually sufficient for the implementation of basic scenarios for using coordinates in applications.
Retrieving Location Data with Qt Positioning
Description of coordinates in the Positioning API
To describe the coordinates, the
Position type is used, which contains a large number of fields that can be divided into several groups. It is worth noting that not all the
Position fields are necessarily specified.
')
The first group of fields includes location information: it includes the
coordinate property, which refers to an instance of the corresponding
coordinate type, as well as the
altitudeValid ,
latitudeValid and
longitudeValid properties, which show whether the corresponding properties were obtained or not. Also for each coordinate time is stored (
timestamp ), when it was received.
The second group includes properties associated with the coordinates and which can be additionally obtained from a GPS sensor or calculated by aggregating a sequence of measured coordinates: the speed of movement on the earth's surface (
speed ), the speed of vertical movement (
verticalSpeed ), the direction of movement (
direction ), as well as corresponding properties with the suffix
Valid , which indicate whether the corresponding properties were obtained.
The type of
coordinate itself is quite simple and contains information about the geographical position (
latitude ,
longitude ,
altitude ) and whether the specified width and longitude are correct (
isValid ). The type provides additional methods for calculating the distance between coordinates (
distanceTo ), azimuth between coordinates (
azimuth ), as well as calculating coordinates by distance and azimuth (
atDistanceAndAzimuth ).
Source of geographic location data
To obtain the current coordinates of the device, you must use the
PositionSource type. This object provides access to the current position through the
position property. While the source for the information is not installed, this property is empty.
First, you need to determine the source of information that is required for the application: is it enough approximate coordinates, derived from the towers of mobile operators, or high positioning accuracy is required using global positioning systems. You can refer to the
supportedPositionMethods property in order to find out which methods are supported by the platform:
- No PositioningMethods - no methods are supported.
- Satellite PositioningMethods - you can get data from the satellite.
- NonSatellitePosititiongMethods - you can get data from the mobile network.
- AllPosititiongMethods - all types of positioning are supported.
Devices running the Sailfish OS operating system support all the methods listed above. And you can select the required ones by setting the
preferredPosititioningMethods property to one of the above values. These sources will be used first, then the default source. If none of the listed sources is available, then the
valid property will be
false .
It should be noted that on the Sailfish OS emulator you will not be able to use any of the data sources listed above. To solve this problem you will have to resort to a separate mechanism, which we will discuss later.
The frequency of updating location information is determined using the
updateInterval property. You should not set this value too short - too frequent updates will lead to excessive power consumption. To control data acquisition, you can use the following methods:
- start to start;
- stop to stop;
- NonSatellitePosititiongMethods - you can get data from the mobile network.
- update to force a location update.
You can also
start and stop in a declarative way - by setting the
active property: assigning a positive value to it is equivalent to calling the
start method.
When the data source is active, it starts updating the
position property. It can be bound to other properties using the binding mechanism in QML. In addition, you can use the
onPositionChanged signal to process new coordinate values ​​as they are received from the data source.
Using Qt Positioning in Sailfish OS
To use the geographic location information in your applications, you need to add the
qt5-qtdeclarative-import-positioning package to the list of dependencies (if only native classes are used, then the
qt5-qtpositioning package is sufficient). It must be specified in the
Requires list of the YAML file of your project. After adding the content may look like this:
Requires: - sailfishsilica-qt5 >= 0.10.9 - qt5-qtdeclarative-import-positioning
Then in the QML file we need to connect the Qt Positioning module and create a
PositionSource element. Consider a short example.
import QtPositioning 5.3 PositionSource { id: src updateInterval: 1000 active: true onPositionChanged: { var coordinate = src.position.coordinate; console.log("Coordinate:", coordinate.longitude, coordinate.latitude); } }
In this example, we do not configure most of the
PositionSource properties and use the default values. Only the
active property is
configured , which is set to
true . As soon as the page with this element is loaded, the location search will be started.
We define an
onPositionChanged event
handler , in which we first access the current coordinate, and then use the resulting link to display the width and longitude of the current coordinate. In addition, you can simply use
console.log (coordinate) , in which case the string will be displayed as degrees and minutes.
Debugging PositionSource Logic
As mentioned earlier, it is impossible to get the location data when you run the application on the emulator. Also during the development of the application, you will most likely want to save time, which is spent on the initial determination of the location each time the corresponding page is opened.
To solve these problems, it is convenient to use the
nmeaSource property of the
PositionSource type. This property accepts the URL of the file in which the GPS track in
NMEA format is recorded. This format is less common here compared to GPX, however, you can use one of the online converters to get such files from your existing GPX tracks. Or create a NMEA-track, using, for example, the service
NMEA Generator .
After you have received the file, you must add it to the application package. To do this, it is enough to specify it as a resource to be installed in the project
pro file using the following construction (you can read more
here ):
OTHER_FILES += nmea/*
nmea.files = nmea/*.nmea
nmea.path = /usr/share/$$TARGET/nmea
INSTALLS += nmea
Thus, after installing the application, this file will also be available on the device. Using this property might look like this:
PositionSource { id: positionSource active: true nmeaSource: "/usr/share/%1/nmea/path.nmea".arg(Qt.application.name) }
In this case, the
PositionSource will provide data from the specified file. Therefore, using the
nmeaSource property,
you can also display NMEA tracks already stored on the device. It is important to remember that setting this value disables the
PositionSource object from the actual data source. If you want to use a real source, you will have to recreate such an object.
Now you can implement any form of tracking the coordinates of your users, as well as realize the display of this information on maps.
Displaying maps and information on them using Qt Location
The
Qt Location module provides a wide range of classes that allow a user to display a map, information on this map, build routes, and search for points of interest (POI) relative to the current geographic coordinate. A good introduction to most of the features of this module can be found in the
official documentation . We cannot highlight all the features of working with maps, so we’ll dwell only on the following points: how to organize the display of the map in the application, how to show the marker on the map and how to show the distance traveled.
Overview of Qt Location Key Types
In our scenario, the key element is the map,
Map . This element allows you to display an image of the Earth's surface from a satellite or a geographical map. Sources of image data and other useful information are
plug-ins that can be provided by platform developers as well as independent developers.
Two plug-ins are available on the Sailfish OS platform:
OpenStreetMap and
HERE WeGo . You can choose one of them by setting the card's
plugin property to
osm or
here, respectively (to use
here, you must obtain an API key). The choice of maps depends on the quality of their coverage for the specific areas in which you intend to use the application. You also need to specify the required plugin in the dependencies of your application, as will be shown below.
To control the map viewing area, you must use the
center property, which indicates the coordinates of the center of the image, as well as the
zoomLevel property, which indicates the zoom level. In the
center property, you need to write the coordinates in accordance with the type of
coordinate , which we mentioned earlier in the section on working with the Positioning API.
Map display in Sailfish OS
To use the Qt Location module in your application, you must ensure that at least one of the geographic data providers is installed:
Requires: - qt5-qtdeclarative-import-location - qt5-plugin-geoservices-osm - qt5-plugin-geoservices-here
Of the last two packages, you can leave one if you plan to use only one provider of geographic information.
Now you can place all the necessary components on the application page. Consider a minimal map display example.
import QtPositioning 5.3 import QtLocation 5.0 import Sailfish.Silica 1.0 import QtQuick 2.0 Page { PositionSource { id: positionSource active: true } Plugin { id: osmPlugin allowExperimental: true preferred: ["osm"] } Map { id: map anchors.fill: paren plugin: osmPlugin center: positionSource.position.coordinate } }
First, we connect the Qt Positioning and Qt Location modules so that all the types we need are available on the page.
The page is based on the Page object provided by the Sailfish Silica library. On the page we form a source of location data, which we immediately activate, as well as a plugin for geographic data. As a source of this data we use OpenStreetMap.
The main visible element of the page is the map, which fills all the available space on the page. We configure it as follows: we configure the plugin and associate the center property of the map with the coordinate provided by the data source.
Adding items to the map
There are two approaches to creating elements on the map: static and dynamic. Static is well suited for objects whose number does not change over time. For our application, such objects are the current location indicator and the route traveled. The dynamic elements of the application are the pointers of points of interest that users add on their own.
To display the current location, we used the
MapQuickItem type. It allows you to place on the map surface an arbitrary object, for example, an
Image object displaying a marker. Consider in detail its use.
Map { MapQuickItem { coordinate: positionSource.position.coordinate anchorPoint.x: image.width / 2 anchorPoint.y: image.height sourceItem: Image { id: image width: 35 height: 50 source: "file:buttons/main_marker.png” } } }
We declared this object as a child of the
Map , thus linking them together. To position the object, we specified the
coordinate property, which is associated with the current geographic location of the mobile device. The
anchorPoint property was also used, which allowed us to move the marker image in such a way that its tip points to the current position.
To display the traversed route, we used the
MapPolyLine type, which allows you to display an arbitrary route as a broken line. Let us consider in more detail its application.
Map { MapPolyline { id: mapline line.width: 3 line.color: 'blue } }
An object of this type must be declared as a child of
Map . We also customized the color and width of the current track.
To add new points to the line, the following code responds:
PositionSource { id: positionSource active: true onPositionChanged: { mapline.addCoordinate(position.coordinate) } }
When a change in location occurs, we add the next point to the line, and thus the next section of the traversed route becomes visible to the application user.
To display a set of points of interest, we used the
MapItemView type. This type is an implementation of the MVC approach for maps: you need to determine the model to display, as well as the type for each element of this model. If you are familiar with how to fill the lists of elements in QML, then working with this component is not difficult.
Consider the main aspects of the application of this model in the application.
Map { MapItemView { id: mapView model: MapViewModel { } delegate: mapPlace } Component { id: mapPlace MapQuickItem { coordinate: QtPositioning.coordinate(latitude, longitude) anchorPoint.x: imagePlace.width / 2 anchorPoint.y: imagePlace.height sourceItem: Image { id: imagePlace width: 35 height: 50 source: "file:buttons/additional_marker.png" } } } }
We have defined the
MapItemView object
as a child of the
Map - so its elements will also be displayed on the map. As a model, we used our own
MapViewModel model, which is actually an extension of the standard
ListModel , in which methods are added for adding and updating elements. The elements of the model are objects with the specified properties
latitude and
longitude . To add a new point of interest to the map, you need to call the
model's addView () method, passing in it the point identifier, its latitude and longitude. The implementation of the method is as follows:
function addView(id, latitude, longitude) { append({ id: id, latitude: latitude, longitude: longitude }) }
By the delegate of the
mapView object
, we specified the
mapPlace component, which contains the
MapQuickItem already known to us. The difference of this element from the previous one is that we have to create the
coordinate type explicitly using the appropriate method of the
QtPositioning type, as well as in specifying another image to display the point of interest marker.
Conclusion
This article has reviewed the features of using geolocation tools in the development for Sailfish OS. The application developed in this article can be installed on your phone using the
OpenRepos.net resource. You can also look at the source code of the application, which is available in our
bitbucket repository.