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¶
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¶
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¶
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.
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¶
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.
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¶
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¶
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¶
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.