Skip to content

Interactive Route Map Coverage

In this guide you will learn how to check map coverage for a route interactively, and get the list of maps (of regions/countries) covering the route (traversed by the route), including their sizes in bytes, as well as the status showing whether each map has already been downloaded.

Get list of maps traversed by route

Route map coverage example

First, get an API key token, see the Getting Started guide.

Qt should be installed to continue.

The Maps SDK for Qt should be installed, see the Setup Maps SDK for Qt guide.

Overview

Route map coverage example

MapCoverageRouteInteractive demonstrates how easy it is to use MapView to display an interactive map, and add markers interactively at desired coordinates, which are displayed as icons on the map. The markers are used as waypoints to calculate and render a route, and then the map coverage for the route is obtained. The resulting list of maps traversed by the route, along with their sizes in bytes, and the status showing whether each map has already been downloaded, are displayed in a popup.

Optionally, more waypoints can be added after that, the route can be updated, and the new list of traversed maps can be displayed.

How it works

In Qt, go to the File menu and select Open File or Project…

then browse to the MapCoverageRoute example folder and open MapCoverageRoute.pro

You may want to have a look at Setting your API Key to see how to open and configure a project and set your API Key.

Defining the waypoints for the route

Route map coverage example

A route requires at least 2 waypoints, in the order in which they were added, these are for the departure and the destination point. There can be 0 or more intermediate waypoints along the route, through which the route passes, in the order in which they were added.

Waypoints are added interactively by the user via long-tap on the map.

1LandmarkList {
2    id: routingWaypoints
3}

First, a LandmarkList is defined and given an id, to hold the waypoints.

 1RoutingService {
 2    id: routingService
 3    type: Route.Type.Fastest
 4    transportMode: Route.TransportMode.Car
 5    // The list of waypoints is made available to the routing service.
 6    waypoints: routingWaypoints
 7    onFinished: {
 8        mapView.routeCollection.set(routeList);
 9        mapView.centerOnRouteList(routeList);
10    }
11}

A RoutingService is defined to compute and render the route on the map.

1SearchService {
2    id: reverseGeocoding
3    searchMapPOIs: true
4    searchAddresses: true
5    limit: 10
6    thresholdDistance: 500
7
8    onSearchCompleted: addWaypointPopup.open()
9}

A SearchService is defined to enable reverse geocoding, to find POIs on the map around the location long-tapped by the user. A popup is shown after a long-tap, containing a scrollable list of up to 10 POIs around the user selected location, of which up to 3 are shown at a time in the popup.

 1TapHandler {
 2    grabPermissions: PointerHandler.CanTakeOverFromAnything
 3    onTapped: {
 4        let coordinates = mapView.wgsForScreen(point.pressPosition);
 5        console.log("x,y (" + point.pressPosition.x + ", " + point.pressPosition.y + ")"
 6                    + " lon, lat " + coordinates.longitude + ", " + coordinates.latitude);
 7        lonlatPositionDisplay.text = "x,y (" + point.pressPosition.x + ", " + point.pressPosition.y + ")"
 8                + " lon, lat " + coordinates.longitude + ", " + coordinates.latitude;
 9    }
10    // interactive selection of waypoints by long-press on the map
11    onLongPressed: {
12        if (point.pressPosition.x + addWaypointPopup.width > mapView.width)
13            addWaypointPopup.x = point.pressPosition.x - addWaypointPopup.width;
14        else
15            addWaypointPopup.x = point.pressPosition.x
16
17        if (point.pressPosition.y + addWaypointPopup.height > mapView.height)
18            addWaypointPopup.y = point.pressPosition.y - addWaypointPopup.height;
19        else
20            addWaypointPopup.y = point.pressPosition.y
21
22        reverseGeocoding.coordinates = mapView.wgsForScreen(point.pressPosition);
23        reverseGeocoding.search();
24    }
25}

In the MapView code block, a TapHandler is defined to handle the onLongPressed signal. This passes the viewport x,y position, converted to longitude,latitude using the wgsForScreen() function, to the reverse geocoding search defined above.

Route map coverage example
 1Popup {
 2    id: addWaypointPopup
 3    modal: true
 4    focus: true
 5    closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
 6    width: 300
 7    height: 200
 8
 9    ListView {
10        id: searchList
11        anchors.fill: parent
12        anchors.margins: 1
13        clip: true
14        model: reverseGeocoding
15
16        delegate: RowLayout {
17            id: row
18            height: 48
19            width: searchList.width
20            IconView {
21                iconSource: landmark.icon
22                Layout.maximumHeight: row.height
23                Layout.maximumWidth: row.height
24                width: height
25                height: row.height
26            }
27            ColumnLayout {
28                Layout.fillHeight: true
29                Layout.fillWidth: true
30                Text {
31                    Layout.fillWidth: true
32                    text: landmark.name + " (" + distance(landmark.coordinates.distance(reverseGeocoding.coordinates)) + ")"
33                    wrapMode: Text.WrapAnywhere
34                }
35                Text {
36                    Layout.fillWidth: true
37                    text: landmark.description
38                    font.italic: true
39                    wrapMode: Text.WrapAnywhere
40                }
41            }
42            TapHandler {
43                target: row
44                onTapped: {
45                    // This adds a waypoint to the route by selecting
46                    // an item from the popup list of reverse geocoding results.
47                    addWaypointPopup.close()
48                    routingWaypoints.append(landmark)
49                    show_waypoint_landmarks_on_map();
50                }
51            }
52        }
53   }
54}

The reverse geocoding search results in up to 10 items, shown in a popup, in a scrollable list, showing up to 3 items at a time. The user can cancel the selection by clicking on the map outside the popup, as per the closePolicy shown above, and the popup is closed.

Conversely, if the user clicks on one of the POIs in the list to select it, see the onTapped signal in the TapHandler at the bottom, then the POI (landmark) is added as a waypoint to the routingWaypoints landmark list defined above.

Rendering the waypoints for the route on the map

Route map coverage example

The show_waypoint_landmarks_on_map() function is called after each waypoint is added, to render the previously selected waypoints, including the most recently selected one, on the map, for visual feedback.

 1function show_waypoint_landmarks_on_map()
 2{
 3    //show landmarks on map as point markers
 4    //a markercoordlist is created, and the coordinates of each interactively selected
 5    //waypoint/landmark is added to the markercoordlist which is used to render the
 6    //sketches-type markers on the map for visual feedback;
 7    let markercoordlist = ServicesManager.createMarker();
 8    for (let n = 0; n < routingWaypoints.length; n++) {
 9        let lmkit = routingWaypoints.get(n)
10        markercoordlist.append(lmkit.coordinates);
11    }
12    //the marker render settings contain configuration such as size and icon image
13    let markerRenderSettings = ServicesManager.createMarkerRenderSettings();
14    //sketches-type markers - cannot be grouped;
15    //let marker = ServicesManager.createMarker();
16    markerRenderSettings.imageSize = 32;
17    //the following line sets a custom icon image;
18    //choose one icon image line and uncomment it;
19    //the icon image is configured in qml.qrc, and located in the project directory;
20    //if an icon image is not set, markers will be shown as blue dots;
21    markerRenderSettings.icon = ServicesManager.createIconFromFile("qrc:/violetcircle.png");
22    //markerRenderSettings.icon = ServicesManager.createIconFromId(Icon.RedBall);
23    //markerRenderSettings.icon = ServicesManager.createIconFromId(Icon.GreenBall);
24    //markerRenderSettings.icon = ServicesManager.createIconFromId(Icon.BlueBall);
25    console.log(markerRenderSettings);
26
27    //SKETCHES-type markers - cannot be grouped, all markers are rendered at all zoom levels;
28    //render on the map the coordinate points for which map coverage is checked
29    let sketchlist = mapView.markerCollection.getExtendedList(MarkerList.Type.Point);
30    sketchlist.clear(); //clear previous markers so list does not grow with duplicates
31    sketchlist.append(markercoordlist, markerRenderSettings);
32
33    //print some debug info to the console
34    console.log("marker sketchlist len:" + sketchlist.length + " icon coords:" + sketchlist.get(0).length);
35    console.log("marker len:"+markercoordlist.length+" renderset:"+markerRenderSettings);
36
37    //center on list of waypoints/landmarks
38    if (routingWaypoints.length > 1)
39    {
40        mapView.centerOnGeographicArea(routingWaypoints.boundingBox)
41    }
42    return markercoordlist;
43}

There are two types of markers: sketches-type markers and regular markers.

Route map coverage example

The sketches-type markers cannot be grouped, and all of them are displayed on the map, regardless of the zoom level. A custom icon image can be configured for rendering the icons. If no image is configured, a blue filled circle will be used as the icon. Sketches-type markers are used in this example.

To see an example using both sketches-type (non-grouping) icons, and grouping marker icons, see the MarkersProgrammatic example.

Setting the custom icon image for sketches-type markers - see qml.qrc and main.qml:

1let markerRenderSettings = ServicesManager.createMarkerRenderSettings();
2markerRenderSettings.icon = ServicesManager.createIconFromFile("qrc:/violetcircle.png");

Alternatively, instead of using a custom icon image, sketches-type markers can also be rendered on the map using the predefined red- green- or blue- filled circles, specified by enum value - see main.qml:

markerRenderSettings.icon = ServicesManager.createIconFromId(Icon.GreenBall);

The custom icon images, such as png images, optionally with transparency, and map styles must be located in the project directory and configured in the qml.qrc file, shown below:

1<RCC>
2   <qresource prefix="/">
3      <file>main.qml</file>
4      <file>redcircle.png</file>
5      <file>greensquare.png</file>
6      <file>bluetriangle.png</file>
7      <file>violetcircle.png</file>
8   </qresource>
9</RCC>

Rendering the route on the map

Route map coverage example

Once at least 2 waypoints have been interactively selected by the user, a route can be rendered:

 1Button {
 2    enabled: routingWaypoints.length > 1
 3    text: qsTr("Render route")
 4    background: Rectangle {
 5        opacity: parent.hovered ? 1 : 0.5
 6        color: enabled ? parent.down ? "#aa00aa" :
 7                                       (parent.hovered ? "#0000ff" : "#2000ff") : "#aaaaaa"
 8    }
 9    palette { buttonText: "#ffffff"; }
10    onClicked: {
11        //////////////////////////////////////////////////////////////////////////////////
12        //render route when button is clicked only if list has at least min 2 waypoints;
13        //////////////////////////////////////////////////////////////////////////////////
14        if (routingWaypoints.length > 1)
15        {
16            //compute and render a route plotted using list of waypoints/landmarks
17            //in the order in which they were added
18            routingService.update()
19        }
20    }
21}

Requesting the list of maps traversed by the route

Route map coverage example

After the route is rendered, the list of maps traversed by the route can be requested:

 1Button {
 2    enabled: routingService.routeList.length > 0
 3    text: qsTr("Maps covering this route")
 4    background: Rectangle {
 5        opacity: parent.hovered ? 1 : 0.5
 6        color: enabled ? parent.down ? "#aa00aa" :
 7                                       (parent.hovered ? "#0000ff" : "#2000ff") : "#aaaaaa"
 8    }
 9    palette { buttonText: "#ffffff"; }
10    onClicked: {
11        //the result returns asynchronously in the mapscoveringroute block below
12        contentStore.intersectsRoute(routingService.routeList[0])
13    }
14}

Note that a route computation returs a route list of possibly more than one route, so the first route, at index 0, is autoselected.

This is done using the intersectsRoute() function from the ContentStore which listens for the onStatusChanged signal:

 1Connections {
 2    target: updater
 3    onFinished: {
 4        if (result === GeneralMagic.Result.Ok || result === GeneralMagic.Result.UpToDate)
 5            contentStore.update();
 6        else
 7            console.error("Content update failed");
 8    }
 9}
10ContentStore {
11    id: contentStore
12    type: ContentItem.Type.RoadMap //content.type
13    onStatusChanged: {
14        if ( routingWaypoints.length > 1 )
15            traversedMapsPopup.open()
16    }
17}

When this signal is received, the content store internal map list is populated with the results, which in this case is the list of maps traversed by the specified route. Note that this is a list of maps, not necessarily of countries. A map may contain an entire country, or only a part, or region, of a country.

Displaying the list of maps traversed by the route

Route map coverage example

The list of maps traversed by the given route is displayed in a popup and is scrollable. The size in bytes of each map is also shown.

 1Popup {
 2    id: traversedMapsPopup
 3    modal: true
 4    focus: true
 5    closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
 6    width: 400
 7    height: 300
 8    anchors.centerIn: parent
 9    ColumnLayout {
10        anchors.fill: parent
11        anchors.top: parent.TopLeft
12        RowLayout {
13            Layout.fillWidth: true
14            CheckBox {
15                id: localMapsCheckBox
16                text: "Local content only / Show only downloaded maps"
17                onCheckedChanged: storeList.model.localContentOnly = checked
18            }
19        }
20        RowLayout {
21            spacing: 10
22            Layout.margins: 10
23            Label {
24                text: localMapsCheckBox.checked ? "Maps traversed by this route which are also downloaded"
25                                                : "All maps traversed by this route"
26            }
27            Button {
28                visible: contentStore.status !== GeneralMagic.Result.Ok
29                text: "Update failed, refresh?"
30                onClicked: contentStore.update()
31            }
32        }
33        Item {
34            Layout.fillHeight: true
35            Layout.fillWidth: true
36            ColumnLayout {
37                anchors.fill: parent
38                ListView {
39                    id: storeList
40                    clip: true
41                    Layout.fillHeight: true
42                    Layout.fillWidth: true
43                    model: contentStore
44                    delegate:
45                        ItemDelegate {
46                        id: control
47                        width: list.width
48                        contentItem: RowLayout {
49                            id: resultmaprow
50                            ColumnLayout {
51                                Layout.fillHeight: true
52                                Layout.fillWidth: true
53                                Text {
54                                    font.pixelSize: 12
55                                    color: "#8000ff"
56                                    text: "map: " + modelData.name
57                                }
58                                Text {
59                                    font.pixelSize: 12
60                                    color: modelData.completed ? "#008080" : "#ff00ff"
61                                    text: modelData.totalSize + " bytes"
62                                }
63                                Text {
64                                    font.pixelSize: 12
65                                    color: modelData.completed ? "#008080" : "#ff00ff"
66                                    text: modelData.completed ? "Local / downloaded / ready"
67                                                              : "Online - need to download!"
68                                }
69                            }
70                            TapHandler {
71                                target: resultmaprow
72                                onTapped: {
73                                    traversedMapsPopup.close()
74                                }
75                            }
76                        }
77                    }
78                }
79            }
80        }
81    }
82}

Click outside the popup to close it. More waypoints can be added by long-tap on the map. Then the route can be updated, and then the list of maps traversed by the updated route can be obtained as shown above.

See the ContentDownload example to see how to download maps from the content store.

QML Examples

Maps SDK for Qt Examples can be downloaded or cloned with Git