Maps and navigation are essential components of many apps. You can find the best restaurant around the corner, track where you wandered on your latest hike or view your travel pictures on a map with your destinations. Of course, they are also crucial in the automotive industry – just imagine a modern car without map and navigation capabilities.

In this article, you can find out how to add navigation and map features into your Qt applications with just 7 quick and easy steps. By following this step-by-step guide, you will learn how to create a location based app:

Let's start with the basics and see how to use map and location features in your app.

Step 1: Add the AppMap to Your Application

Adding a Map

The first step is pretty straightforward. You just need to add an AppMap item to your app:


import Felgo 3.0
import QtQuick 2.12
import QtLocation 5.12

App {
 AppMap { anchors.fill: parent }
}

 

Step 2: Choose Your Map Provider

Now, after adding a plugin and you should see a simple, but functioning map feature:

Test this example on your mobile phone now! Run This Example

import Felgo 3.0
import QtQuick 2.12
import QtLocation 5.12

App {
 AppMap {
   anchors.fill: parent

   // configure map provider
   plugin: Plugin {
     name: "mapbox"
     // configure your own map_id and access_token here
     parameters: [
       PluginParameter {
         name: "mapbox.mapping.map_id"
         value: "mapbox.streets"
       },
       PluginParameter {
         name: "mapbox.access_token"
         value: "pk.eyJ1IjoiZ3R2cGxheSIsImEiOiJjaWZ0Y2pkM2cwMXZqdWVsenJhcGZ3ZDl5In0.6xMVtyc0CkYNYup76iMVNQ"
       }
     ]
   }
 }
}

 

Navigation and Map Feature  - Add the AppMap to your application

Configuring the Plugin

Besides Mapbox, there are also other map providers you can choose from. Currently, QML supports the following plugins:

The main QML type to use here is the Plugin item which lets you access various map APIs.

In the case of the Mapbox plugin, you need to generate an access token on the provider website. Next, you’ll need to configure the plugin by creating PluginParameter objects with necessary values. In the case of Mapbox, you will need to provide access token and map ID.

If you want to try other map providers, all you need do is to change your plugin. Let’s give OpenStreetMap a shot. To make it work, just update the plugin name and parameters as shown below.

Test this example on your mobile phone now! Run This Example

import Felgo 3.0
import QtQuick 2.12
import QtLocation 5.12

App {
 AppMap {
   anchors.fill: parent

   // configure map provider
   plugin: Plugin {
     name: "osm"
     PluginParameter { name: "osm.useragent"; value: "OSM usage example" }
     PluginParameter { name: "osm.mapping.host"; value: "http://osm.tile.server.address/" }
     PluginParameter { name: "osm.routing.host"; value: "http://osrm.server.address/viaroute" }
   }
 }
}

 

You can see that the look of the map has changed, but it is not the only thing. Various providers may expose different kinds of data and features: more precise routing, or access to geocoding.

 

Navigation and Map Feature - Configuring the Plugin

 

If you want to further customize the map behavior, PluginParameters is the way to go. Create another PluginParameter item and then set the name and value parameters. With them, you can change things like:

  • Memory cache size 
  • Cost strategy
  • Directory of offline tiles storage

You can find more info about using plugin items with maps in Felgo documentation.

Tailor-made Qt training and workshops just for you. Get a free quote today!

Step 3: Design Your AppMap

Customizing Map

Changing the map supplier is not the only way to modify map appearance. You can simply set activeMapType property in AppMap to any value from the supportedMapTypes list. The available themes may vary according to the map provider.

Test this example on your mobile phone now! Run This Example


import Felgo 3.0
import QtQuick 2.12
import QtLocation 5.12

App {
 property int mapTypeIndex: 1

 AppMap {
   id: appMap
   anchors.fill: parent

   activeMapType: supportedMapTypes[mapTypeIndex]
   plugin: mapboxPlugin
 }

 Plugin {
   id: mapboxPlugin
   name: "mapbox"
   // configure your own map_id and access_token here
   parameters: [
     PluginParameter {
       name: "mapbox.mapping.map_id"
       value: "mapbox.streets"
     },
     PluginParameter {
       name: "mapbox.access_token"
       value: "pk.eyJ1IjoiZ3R2cGxheSIsImEiOiJjaWZ0Y2pkM2cwMXZqdWVsenJhcGZ3ZDl5In0.6xMVtyc0CkYNYup76iMVNQ"
     }
   ]
 }

 IconButton {
   icon: IconType.chevronright
   size: dp(36)
   anchors {
     right: parent.right
     verticalCenter: parent.verticalCenter
     margins: dp(8)
   }
   // increase or start at 0 again if max index exceeded
   onClicked: mapTypeIndex = (mapTypeIndex + 1) % appMap.supportedMapTypes.length
 }

 IconButton {
   icon: IconType.chevronleft
   size: dp(36)
   anchors {
     left: parent.left
     verticalCenter: parent.verticalCenter
     margins: dp(8)
   }
   // decrease index or jump to max index if at the beginning
   onClicked: mapTypeIndex = (mapTypeIndex === 0) ? appMap.supportedMapTypes.length - 1 : mapTypeIndex - 1
 }
}

 

Let’s see how the map looks like when the appearance is changed.

 

Navigation and Map Feature - Design your AppMap

 

You can also programmatically adjust the zoom level or the center coordinates  via properties. To center map in a certain location, you need to use the Qt Positioning module. Just call the static coordinate method and pass the place latitude and longitude as parameters.

Test this example on your mobile phone now! Run This Example


import Felgo 3.0
import QtQuick 2.12
import QtLocation 5.12
import QtPositioning 5.12

App {
 AppMap {
   anchors.fill: parent
   center: QtPositioning.coordinate(59.91, 10.75) // Oslo
   zoomLevel: 14
   plugin: mapboxPlugin
 }

 Plugin {
   id: mapboxPlugin
   name: "mapbox"
   // configure your own map_id and access_token here
   parameters: [
     PluginParameter {
       name: "mapbox.mapping.map_id"
       value: "mapbox.streets"
     },
     PluginParameter {
       name: "mapbox.access_token"
       value: "pk.eyJ1IjoiZ3R2cGxheSIsImEiOiJjaWZ0Y2pkM2cwMXZqdWVsenJhcGZ3ZDl5In0.6xMVtyc0CkYNYup76iMVNQ"
     }
   ]
 }
}

Navigation and Map Feature - adjust the zoom level

Step 4: Show User Position

Displaying User Position

 

This is great, but where is the user? You can display its position with just two properties in AppMap:

Test this example on your mobile phone now! Run This Example

import Felgo 3.0
import QtQuick 2.12
import QtLocation 5.12
import QtPositioning 5.12

App {
 AppMap {
   anchors.fill: parent
   showUserPosition: true
   enableUserPosition: true

   plugin: Plugin {
     name: "mapbox"
     // configure your own map_id and access_token here
     parameters: [
       PluginParameter {
         name: "mapbox.mapping.map_id"
         value: "mapbox.streets"
       },
       PluginParameter {
         name: "mapbox.access_token"
         value: "pk.eyJ1IjoiZ3R2cGxheSIsImEiOiJjaWZ0Y2pkM2cwMXZqdWVsenJhcGZ3ZDl5In0.6xMVtyc0CkYNYup76iMVNQ"
       }
     ]
   }

   // check for user position initially when the component is created
   Component.onCompleted: moveToUserPosition()

   // once we successfully received the location, we zoom to the user position
   onUserPositionAvailableChanged: moveToUserPosition()

   function moveToUserPosition() {
     if(userPositionAvailable) {
       center = userPosition.coordinate
        zoomLevel = 13
     }
   }
 }
}

 

Navigation and Map Feature - Show User Position

 

Note: If you are deploying to Android, don’t forget to add ACCESS_FINE_LOCATION permission in the manifest!

Customizing the indicator is also fairly simple. All you need to do is to create MapQuickItem and bind a coordinate property to the user coordinates. The user position will update automatically and the marker position will follow.

Step 5: Add Searchable Address Field

Searching the Address

In many cases, you need to find an address given by the user and display it on the map. To do so, you need to use the GeocodeModel. Before examining this object, you need to know something about the process itself.

Geocoding

To put it in a few words – it’s a process of matching addresses or search strings with geographic locations. This procedure is essential for navigation. Don’t worry, using it in Qt is not complicated at all.

In the beginning, you need the GeocodeModel inside the app. Remember that the model won’t perform geocoding out of thin air – you need to provide a map plugin to process the data. To get the coordinates of the address, just input its name to model query. The location data will update automatically.

Test this example on your mobile phone now! Run This Example

import QtQuick 2.12
import QtLocation 5.12
import QtPositioning 5.12
import Felgo 3.0

App {
Page {
 AppMap {
   id: appMap
   anchors.fill: parent

   // configure map provider
   plugin: mapboxPlugin

   MapItemView {
     id: mapItemView
     model: geocodeModel
     delegate: markerDelegate
   }

   //Component used as delegate
   Component {
     id: markerDelegate
     MapQuickItem {
       coordinate: locationData.coordinate
       anchorPoint {
         x: sourceItem.width / 2
         y: sourceItem.height
       }

       //Item used as graphic
       sourceItem: Icon {
         id: userMarker
         icon: IconType.mapmarker
         color: "red"
         size: dp(30)
       }
     }
   }
 }

 Plugin {
   id: mapboxPlugin
   name: "mapbox"
   // configure your own map_id and access_token here
   parameters: [
     PluginParameter {
       name: "mapbox.mapping.map_id"
       value: "mapbox.streets"
     },
     PluginParameter {
       name: "mapbox.access_token"
       value: "pk.eyJ1IjoiZ3R2cGxheSIsImEiOiJjaWZ0Y2pkM2cwMXZqdWVsenJhcGZ3ZDl5In0.6xMVtyc0CkYNYup76iMVNQ"
     }
   ]
 }

 GeocodeModel {
   id: geocodeModel
   plugin: mapboxPlugin
   autoUpdate: true
   onLocationsChanged: {
     appMap.center = get(0).coordinate
     appMap.zoomLevel = 10
   }
 }

 SearchBar {
   onAccepted: geocodeModel.query = text
 }

 AppActivityIndicator {
   id: busyIndicator
   anchors.centerIn: parent
   visible: geocodeModel.status == GeocodeModel.Loading
 }
}
}

 

Navigation and Map Feature - Geocoding


Now that you finally have some address data, how can you display it? The MapItemView comes to save the day! Add it inside the AppMap item, bind the model to GeocodeModel, create a delegate, and voila! You can now see the addresses on the map.

Reverse Geocoding

There might be a case when you want to indicate the address of the place selected by the user on the map. It’s a similar process to geocoding but reversed. Instead of matching addresses to coordinates, you match coordinates to addresses.

In this scenario, you can also use GeocodeModel. Instead of providing the address, you can simply pass map coordinates and the model will take care of the rest. To transform in-app coordinates to map coordinates, use the toCoordinate method from the AppMap item. After a query is complete, you can access the address data from the Location object returned by the get function. Piece of cake, right?

Test this example on your mobile phone now! Run This Example

import QtQuick 2.12
import QtPositioning 5.12
import QtLocation 5.12
import Felgo 3.0

App {
 AppMap {
   id: appMap
   anchors.fill: parent
   plugin: mapboxPlugin

   MouseArea {
     anchors.fill: parent
     onPressAndHold: {
       marker.visible = false
       var coordinates = appMap.toCoordinate(Qt.point(mouse.x, mouse.y))
       geocodeModel.query = coordinates
       marker.coordinate = coordinates
     }
   }

   MapQuickItem {
     id: marker
     visible: false

     //Item used as graphic
     sourceItem: Item {
       Icon {
         icon: IconType.mapmarker
         color: "red"
         size: dp(30)
         anchors.horizontalCenter: parent.horizontalCenter
         anchors.bottom: parent.bottom
       }
     }
   }
 }

 Plugin {
   id: mapboxPlugin
   name: "mapbox"
   // configure your own map_id and access_token here
   parameters: [
     PluginParameter {
       name: "mapbox.mapping.map_id"
       value: "mapbox.streets"
     },
     PluginParameter {
       name: "mapbox.access_token"
       value: "pk.eyJ1IjoiZ3R2cGxheSIsImEiOiJjaWZ0Y2pkM2cwMXZqdWVsenJhcGZ3ZDl5In0.6xMVtyc0CkYNYup76iMVNQ"
     }
   ]
 }

 GeocodeModel {
   id: geocodeModel
   plugin: mapboxPlugin
   autoUpdate: true
   onLocationsChanged: {
     if(count > 0) {
       marker.visible = true
       var address = get(0).address
       addressLabel.text = address.street + " " + address.city + " " + address.country
     }
   }
 }

 AppText {
   id: addressLabel
   width: parent.width * 0.8
   color: "red"
   anchors {
     top: parent.top
     horizontalCenter: parent.horizontalCenter
     topMargin: dp(20)
   }
   fontSize: 16
   clip: true
   text: qsTr("Hold anywhere on the map to display address")
 }

 AppActivityIndicator {
   id: busyIndicator
   anchors.centerIn: parent
   visible: geocodeModel.status == GeocodeModel.Loading
 }
}

Navigation and Map Feature - Reverse geocoding

Step 6: Set Up the Navigation

Routing

Finding addresses is not the only feature we generally need, routing is also vital for map-based apps. No worries, Qt also has suitable tools to solve this problem. In this case, RouteModel is  priceless.

This type is used similarly to GeocodeModel. After setting the plugin for the map provider, you can create a RouteQuery. There, you simply pass some coordinates and then the model tries to find the optimal way. 

Below, you can see an example where the user can select two points on the map and the app will create a route from the start to the destination point. To visualize this path, you will use the MapRoute item. This time the model is not automatically updated, but requires a manual call to its update function.

Test this example on your mobile phone now! Run This Example

import QtQuick 2.12
import QtLocation 5.12
import QtPositioning 5.12
import Felgo 3.0

App {
 AppMap {
    id: appMap
    anchors.fill: parent

    // configure map provider
    plugin: mapboxPlugin

    //Route visualization item
    MapRoute {
      id: routeItem
      opacity: 0.6
      smooth: true
      antialiasing: true
      visible: false
      line {
        color: "cornflowerblue"
        width: 5
      }
    }

    MapQuickItem {
      id: startMarker
      visible: false
      anchorPoint {
        x: sourceItem.width / 2
        y: sourceItem.height
      }

      //Item used as graphic
       sourceItem: Icon {
         icon: IconType.mapmarker
         color: "red"
         size: dp(30)
       }
    }

    MapQuickItem {
      id: destinationMarker
      visible: false
      anchorPoint {
        x: sourceItem.width / 2
        y: sourceItem.height
      }

      //Item used as graphic
       sourceItem: Icon {
         icon: IconType.mapmarker
         color: "red"
         size: dp(30)
       }
    }

    MouseArea {
      property bool isSettingStartMarker: true
      id: appMapMA
      anchors.fill: parent
      enabled: routeModel.status !== RouteModel.Loading

      onPressAndHold: {
        var coordinates = appMap.toCoordinate(Qt.point(mouse.x, mouse.y))
        if(isSettingStartMarker) {
          routeQuery.addWaypoint(coordinates)
          startMarker.coordinate = coordinates
          startMarker.visible = true

          //Hiding old route
          destinationMarker.visible = false
          routeItem.visible = false
          routeModel.reset()
          isSettingStartMarker = false
        }
        else {
          routeQuery.addWaypoint(coordinates)
          destinationMarker.coordinate = coordinates
          destinationMarker.visible = true
          isSettingStartMarker = true
        }

        /*
        If start and destination marker are set
        the route model need to be updated
        */
        if(routeQuery.waypoints.length >= 2){
          routeQuery.travelModes = RouteQuery.CarTravel
          routeModel.update()
        }
      }
    }
  }

  Plugin {
    id: mapboxPlugin
    name: "mapbox"
    // configure your own map_id and access_token here
    parameters: [
      PluginParameter {
        name: "mapbox.mapping.map_id"
        value: "mapbox.streets"
      },
      PluginParameter {
        name: "mapbox.access_token"
        value: "pk.eyJ1IjoiZ3R2cGxheSIsImEiOiJjaWZ0Y2pkM2cwMXZqdWVsenJhcGZ3ZDl5In0.6xMVtyc0CkYNYup76iMVNQ"
      }
    ]
  }

  RouteModel {
    id: routeModel
    plugin: mapboxPlugin
    autoUpdate: false
    query: routeQuery

    onStatusChanged: {
      //When model found a route pass show it
      if(status == RouteModel.Ready) {
        routeItem.route = get(0)
        routeItem.visible = true
        //Clear old route query
        routeQuery.clearWaypoints()
      }
    }
  }

  RouteQuery {
    id: routeQuery
  }
}

 

Navigation and Map Feature - Customize Your Map


Step 7: Display Route Information

Route details

Showing the route on a map is one thing, but what if you want to display some information associated with it?  get method from RouteModel returns the Route object, contains properties like distance or travelTime. Below, you will find an example of how to display route length using code from the routing section with just a few tweaks.

Test this example on your mobile phone now! Run This Example

import QtQuick 2.12
import QtLocation 5.12
import QtPositioning 5.12
import Felgo 3.0

App {
 AppMap {
   id: appMap

   anchors.fill: parent

   // configure map provider
   plugin: mapboxPlugin

   //Route visualization item
   MapRoute {
     id: routeItem
     opacity: 0.6
     smooth: true
     antialiasing: true
     visible: false
     line {
       color: "cornflowerblue"
       width: 5
     }
   }

   MapQuickItem {
     id: startMarker
     visible: false
     anchorPoint {
       x: sourceItem.width / 2
       y: sourceItem.height
     }

     //Item used as graphic
       sourceItem: Icon {
         icon: IconType.mapmarker
         color: "red"
         size: dp(30)
       }
   }

   MapQuickItem {
     id: destinationMarker
     visible: false
     anchorPoint {
       x: sourceItem.width / 2
       y: sourceItem.height
     }

     //Item used as graphic
       sourceItem: Icon {
         icon: IconType.mapmarker
         color: "red"
         size: dp(30)
       }
   }

   MouseArea {
     property bool isSettingStartMarker: true
     anchors.fill: parent
     enabled: routeModel.status !== RouteModel.Loading

     onPressAndHold: {
       var coordinates = appMap.toCoordinate(Qt.point(mouse.x, mouse.y))
       if(isSettingStartMarker) {
         routeQuery.addWaypoint(coordinates)
         startMarker.coordinate = coordinates
         startMarker.visible = true

         //Hiding old route
         destinationMarker.visible = false
         routeItem.visible = false
         routeModel.reset()
         isSettingStartMarker = false
       }
       else {
         routeQuery.addWaypoint(coordinates)
         destinationMarker.coordinate = coordinates
         destinationMarker.visible = true
         isSettingStartMarker = true
       }

       /*
       If start and destination marker are set
       the route model need to be updated
       */
       if(routeQuery.waypoints.length >= 2){
         routeQuery.travelModes = RouteQuery.CarTravel
         routeModel.update()
       }
     }
   }
 }

 Plugin {
   id: mapboxPlugin
   name: "mapbox"
   // configure your own map_id and access_token here
   parameters: [
     PluginParameter {
       name: "mapbox.mapping.map_id"
       value: "mapbox.streets"
     },
     PluginParameter {
       name: "mapbox.access_token"
       value: "pk.eyJ1IjoiZ3R2cGxheSIsImEiOiJjaWZ0Y2pkM2cwMXZqdWVsenJhcGZ3ZDl5In0.6xMVtyc0CkYNYup76iMVNQ"
     }
   ]
 }

 RouteModel {
   id: routeModel
   plugin: mapboxPlugin
   autoUpdate: false
   query: routeQuery

   onStatusChanged: {
     //When model found a route pass show it
     if(status == RouteModel.Ready) {
       routeItem.route = get(0)
       //passing info about route length
       routeLengthLabel.routeLength = get(0).distance
       routeItem.visible = true

       //Clear old route query
       routeQuery.clearWaypoints()
     }
   }
 }

 RouteQuery {
   id: routeQuery
 }

 AppText {
   property real routeLength: 0

   id: routeLengthLabel
   width: parent.width * 0.8
   visible: routeItem.visible
   anchors {
     bottom: parent.bottom
     horizontalCenter: parent.horizontalCenter
     bottomMargin: dp(20)
   }
   fontSize: 14
   clip: true
   //Displaying route length converted from m to km
   text: qsTr("Route length ") + (routeLength/1000).toFixed(1) + "km"
 }

 AppActivityIndicator {
   id: busyIndicator
   anchors.centerIn: parent
   visible: routeModel.status === RouteModel.Loading
 }
}

Navigation and Map Feature - Routing

Congrats, You’ve Reached Your Destination

Now that you have a fully functioning map within your app, you can easily test the feature using Felgo Hot Reload. At this point, you can still add navigation items, new pages and subpages and adjust the content in your project. You can make any changes right in your running app and immediately see your modifications without ever leaving the screen.

Navigation and Map feature - Hot Code Reload

Watch the QML Hot Reload: the real-time revolution Webinar and Learn How to Update Your App Instantly

GPS Navigation with Real-Life Examples

Your GPS navigation app is now ready to launch in the Android and iOS app stores. To ensure that your app remains successful in the competitive mobile app market, you’ll need to know what your app is up against on Google Play and Apple Store. 

You can also use our list to come up with brilliant mobile app ideas for your next Android and iOS app project. Either way, knowing the major app categories and how they integrate GPS navigation into their application should be a must-know for all app developers. 

GPS App Categories & Purposes

GPS Apps

First and foremost, the most obvious category is the GPS itself. These mobile apps are built to help you find the fastest and easiest route from your starting point to your destination. Besides driving assistance, you can also see public transportation schedules, find restaurants, bars, shops, or attractions in the area. 

A great example of GPS apps is Google Maps, MapQuest or Waze.

Apps for Recommendations

When you are in an unfamiliar place, you will most likely look up the best places to go out to eat or the must-visit attractions in the city. To do so, you will rely on the feedback and recommendation of others. These apps are created to help you find business locations or major attractions. 

Yelp integrated the map and geolocation feature for this purpose. You can easily find the location of the recommended place within the app. You can also search for the easiest way to get there and plan other trips nearby. 

Weather Apps

One of the main functionality of a weather app is the geolocation. With this technology, users can access real-time weather forecasts in their area. If you are thinking about developing your own weather app, geolocation integration is definitely a must-have. 

Gelocation & Weather App

AccuWeather or Felgo’s WTR - Weather Pro is a great example of such apps. You can develop your own weather app using Felgo’s open source weather app example.

Augmented Reality Apps

Adding a navigation feature to your AR app can greatly enhance your mobile application. Opposed to virtual reality, AR enhances the world using advanced technology. For instance, you can mix your GPS navigation with real street images while driving.

Wikitude Drive is one of the best AR navigation apps for Android devices. With Wikitude Drive, users can simply set GPS data as well as point-of-interest (POI). 

Services Provider Apps

If you make an app like Uber, Uber Eats or Lyft, you need to make sure that your users can follow the service they ordered. Users now expect to see the exact location of the cab driver or where the pizza delivery guy stuck in traffic. With the geolocation feature, you enable users to see the estimated time of arrival as well. 


Sport Tracking Apps

Fitness applications use GPS navigation features to enable the users to track their route, measure distance, time or speed. 

Runtastic or MapmyRun are just a few of the fitness apps that take advantage of the GPS features.

Would You Like To Advance Your Skills Further?

Feel free to check out the advanced map and navigation example project on GitHub. It includes all of the above examples, as well as an advanced example that offers a Place Search with auto-suggestion feature:

via-rome-navigation-feature

You should also check out the Felgo documentation for more helpful QML items

In case you are considering outsourcing part of your app development, or would like to deepen your knowledge of developing with Qt, Felgo is here to assist you. From professional consulting and software development services to tailor-made app development training and workshops, Felgo can help you bring your app to life.

Download Felgo

 

Related Articles

How To Add Push Notification to your App

How To Add Augmented Reality to your App

How To Add Machine Learning to your App

Qt Tutorial for Beginners

3 Practical App Development Video Tutorials

 

More Posts Like This

qml-hot-reload-with-felgo-live-for-qt (1)

QML Hot Reload for Qt - Felgo

featureimagebestpracticeversion3

Best Practices of Qt on Mobile


Continuous Integration and Delivery (CI/CD) for Qt and Felgo