GeneralMagic QML Manual

The GeneralMagic QML module is a joint effort between KDAB and GeneralMagic to bring to the QML world the excellent GeneralMagic Maps and Navigation SDK.

Best in its class. With a unique combination of features, GeneralMagic Maps and Navigation SDK is sure to fill a niche long sought by users:

  • Easy to use, lightweight, efficient - runs even on Raspberry Pi Zero
  • First class Turn-by-turn navigation optimized for car, pedestrian and bicycle, with optional voice guidance in many languages, each with both male and female voices.
  • Global coverage. Up to date OpenStreetMap maps. 3D terrain topography.
  • Offline maps available for download. Features such as mapping, searching, routing and turn-by-turn navigation work offline, without an internet connection.

Using the GeneralMagic QML module, you can easily write your own customized navigation app, or navigation module within your app. The QML interface is simple, easy to learn, and you can get your own navigation app up and running with only a few lines of code! See examples below.

Getting Started

As the very first step, we highly recommend that you get a token from https://developer.generalmagic.com. If no token is set, you can still test your apps, but a watermark will be displayed, and all the online services including mapping, searching, routing, etc. will slow down after a few minutes.

Setting the token safely

It is your responsibility to keep your token safe! That is why we recommend that you DO NOT set your token as plain text in your .qml files. Instead, we highly recommend that you set your token via a C++ property.

The following C++ and QML snippets demonstrate how to set your token via a C++ property:


  // C++ code
  QQmlApplicationEngine engine;
  //...
  //! [Set token safely]
  // go to https://developer.generalmagic.com to get your token
  engine.rootContext()->setContextProperty("__my_secret_token", "");
  //! [Set token safely]

  engine.load(url);


  // QML code
  // ...
  Component.onCompleted: {
      //! [Set token safely]
      ServicesManager.commonSettings.token = __my_secret_token;
      //! [Set token safely]

      ServicesManager.commonSettings.allowInternetConnection = true; // enable connection to online services
      ServicesManager.contentManager.autoUpdate = true; // enable map data auto update
  }
  // ...

GeneralMagic QML Module Installation Instructions

To load the GeneralMagic QML module, add the following statement to your .qml files


  import GeneralMagic 1.0

Mapping

A MapView provides a map rendering control, which displays an interactive map. The user can manually pan and zoom the map, or the map can be animated automatically, for example, by the simulator. MapView preferences can be used to configure the map. The map view and behavior can be further fine-tuned with MapViewPreferences. MapView takes control of the map camera and automatically sets the position, zoom level, view angle and rotation based on the ServicesManager::currentPosition. MapView::startFollowingPosition is much more efficient than controlling the camera from outside.

This example is a complete working "Hello World" qml app which uses MapView to render an interactive map which the user can control using pan and zoom. Simply create a new Qt Quick Application - Empty project in Qt and replace the contents of main.qml with the code below.


  import QtQuick 2.9
  import QtQuick.Window 2.2
  import GeneralMagic 1.0
  Window {
      visible: true
      width: 640; height: 480
      title: qsTr("Simple Map")
      MapView {
          id: mapView
          anchors.fill: parent
          preferences {
              viewDetails: MapViewPreferences.MapViewDetails.None
              showPOIs: true // show points of interest on the map
              show3DBuildings: true
          }
      }
      Component.onCompleted: {
          ServicesManager.commonSettings.allowInternetConnection = true; // enable connection to map data server
          ServicesManager.commonSettings.mapLanguage = CommonSettings.NativeLanguage;
          ServicesManager.contentManager.autoUpdate = true; // enable map data auto update
          ServicesManager.logLevel = ServicesManager.Error;
      }
  }

Changing the MapViewPreferences.MapViewDetails.None line to MapViewPreferences.MapViewDetails.Terrain activates the terrain/topography rendering.


  //viewDetails: MapViewPreferences.MapViewDetails.None //this loads the standard map
  viewDetails: MapViewPreferences.MapViewDetails.Terrain //this loads the 3D topography/terrain elevation data

A view of the Grand Canyon and the Colorado River.

Mount St. Helens stratovolcano.

There are many properties available such as MapView::gesturesEnabled to enable or disable gestures, or MapView::animationTime to choose the duration of animations.

MapView::preferences options, described in MapViewPreferences, include boolean toggles such as MapViewPreferences::cursorVisibility to enable the cursor, MapViewPreferences::showPOIs to enable rendering points of interest, MapViewPreferences::show3DBuildings to enable rendering 3D buildings, MapViewPreferences::showWeather whether to render the weather conditions on the map.

Searching

Searching can be performed by using SearchService, and AddressSearchService.

All of the searching methods have a filter property which, used in combination with the search method, finds results matching the specific string query.

SearchService and AddressSearchService both inherit from LandmarkModel, which allows for them to be used as models for any QML views. The landmark attached property will be available in your delegate.

Routing

This example is a snippet which can be added to the above Mapping example, inside the Window block, at the same level as the MapView block, to make a complete working "Hello World" qml app which uses RoutingService and MapView to render a route on an interactive map which the user can control using pan and zoom.

Routing uses LandmarkList as waypoints which are set in RoutingService::waypoints and then the RoutingService creates available Route variants which are accessible in RoutingService::routes.


  RoutingService {
      preferences {
          routeType: RoutePreferences.Fastest
          transportMode: RoutePreferences.TransportMode.Car
      }
      waypoints: LandmarkList {
          Landmark {
              name: "start"
              coordinates: Coordinates {
                  latitude: 48.849289 //Paris
                  longitude: 2.346027
              }
          }
          Landmark {
              name: "stop"
              coordinates: Coordinates {
                  latitude: 48.874630 //Paris
                  longitude: 2.331512
              }
          }
      }
      onCompleted: {
          mapView.preferences.routesCollection.set(routes);
          mapView.centerOnRoutes(routes);
      }
      Component.onCompleted: update()
  }
  MapView {
      // ...
      onRouteSelected: preferences.routesCollection.mainRoute = route //add this line to the MapView block in the Mapping example
  }

Commenting out the Paris start and stop coordinates in the example above, and replacing them with


  latitude: 37.4184567 //SF bay area route start
  longitude: -122.0882378

  latitude: 37.8586084 //SF route stop
  longitude: -122.5814328

and leaving the terrain/topography rendering active, we get this route.

The NavigationService begins navigation with the set parameters along a Route from RouteService, when NavigationService::active is set to true or NavigationService::start is called.

During navigation, any events related to the journey, such as the destination being reached, are emitted through NavigationListener.

The following snippet shows how to get the current location of the device.


  import QtPositioning 5.12 as QPos

  ...
  QPos.PositionSource {
      id: positionSource
      active: true
      Component.onCompleted: {
          ServicesManager.setPositionSource(positionSource);
          ServicesManager.loadLastKnownPosition();
      }
      Component.onDestruction: ServicesManager.saveLastKnownPosition();
  }

The current location can be set in the starting waypoint for the route like this.


  Landmark {
      name: "start"
      coordinates: ServicesManager.currentPosition
  }

The NavigationService could be used in the following way with the routing snippet.


  NavigationService {
      route: mapView.preferences.routeCollection.mainRoute
      navigationListener: NavigationListener {
          onDestinationReached: {
              mapView.preferences.routesCollection.clear();
              console.log("DestinationReached: " + waypoint.name);
          }
          onRouteUpdated: {
              console.log("RouteUpdated: " + route.summary);
              mapView.preferences.routesCollection.clear();
              mapView.preferences.routesCollection.add(route);
          }
          onNavigationInstructionUpdated: {
              console.log("nextTurnInstruction: " + navigationInstruction.nextTurnInstruction);
              console.log("nextStreetName: " + navigationInstruction.nextStreetName);
              console.log("distanceToNextTurn: " + navigationInstruction.distanceToNextTurn);
          }
      }
      onActiveChanged: active ? mapView.startFollowingPosition() : mapView.stopFollowingPosition()
  }

Simulation

Unlike NavigationService, SimulationService doesn't use the ServicesManager::currentPosition, instead, the position is synthetic, and controlled by SimulationService, using the given Route for navigation simulation. There are properties to control the SimulationService::speed and SimulationService::speedFactor as well as a property to SimulationService::pause the simulation.

Continuing from the previous example, we first need to add another import statement at the top.


  import QtQuick.Controls 2.0 //for Button

This example is a snippet which can be added to the above Mapping example, inside the Window block, at the same level as the MapView block, after first adding the above Routing example, to make a complete working "Hello World" qml app which uses RoutingService and MapView to render a route on an interactive map, which the user can control using pan and zoom, and then use automated animation to simulate navigation along the route from start to finish. The navigation simulation animation starts when the user clicks the button in the upper left corner of the screen, and stops upon reaching the endpoint, or if the user clicks the button again.


  SimulationService {
      id: simulationService
      route: mapView.preferences.routesCollection.mainRoute
      onActiveChanged: if (active) mapView.startFollowingPosition(); else mapView.stopFollowingPosition();
      navigationListener: NavigationListener {
          onWaypointReached: console.log("WaypointReached :" + waypoint.name);
          onDestinationReached: {
              mapView.preferences.routesCollection.removeAll();
              console.log("DestinationReached :" + waypoint.name);
          }
          onNavigationError: {
              console.log("NavigationError :" + error);
              mapView.preferences.routesCollection.removeAll();
          }
          onRouteUpdated: {
              console.log("RouteUpdated :" + route.summary);
              mapView.preferences.routesCollection.add(route);
          }
          onPositionStatusUpdated: {
              console.log("PositionStatusUpdated:" + status);
          }
          onNavigationInstructionUpdated: {
              console.log("status :" + navigationInstruction.status);
              console.log("currentStreetName :" + navigationInstruction.currentStreetName);
              console.log("nextStreetName :" + navigationInstruction.nextStreetName);
              console.log("distanceToNextTurn :" + navigationInstruction.distanceToNextTurn);
              console.log("timeToNextTurn :" + navigationInstruction.timeToNextTurn);
              console.log("remainingTravelTime :" + navigationInstruction.remainingTravelTime);
              console.log("remainingTravelDistance :" + navigationInstruction.remainingTravelDistance);
              console.log("remainingTravelTimeToNextWaypoint :" + navigationInstruction.remainingTravelTimeToNextWaypoint);
              console.log("remainingTravelDistanceToNextWaypoint :" + navigationInstruction.remainingTravelDistanceToNextWaypoint);
              console.log("traveledDistance :" + navigationInstruction.traveledDistance);
              console.log("traveledTime :" + navigationInstruction.traveledTime);
          }
      }
  }
  MapView {
      // ...
      Button  {
          text: simulationService.active ? qsTr("Stop simulation") : qsTr("Start simulation")
          onClicked: simulationService.active = !simulationService.active;
      }
  }

Examples