Qt pour Android : Géolocalisation et cartes

Présentation

Qt fournit un ensemble de ressources (classes C++ et composants QML) pour la géolocalisation et l’affichage de cartes.

Localisation

L’API de positionnement Qt est disponible pour Android.

Il faudra ajouter le module dans le fichier de projet .pro :

QT += positioning
  • QML :
import QtPositioning 5.6
  • C++ :
#include <QGeoCoordinate>

Liens :

Exemples :

QML

On dispose d’un type coordonnées qui contient les données suivantes :

  • latitude
  • longitude
  • altitude

Il est aussi possible de créer une coordonnée :

property variant coordonneeAvignon: QtPositioning.coordinate(43.95, 4.8167) // Avignon

On dispose aussi d’un type position contenant donc la coordonnée mais aussi la vitesse, l’horodatage, … et la validation de ces information.

Pour récupérer le positionnement, on utilisera un PositionSource qui fournit des informations sur la position actuelle de la machine.

Exemple :

PositionSource {
    id: positionSource
    updateInterval: 1000 // mise à jour de la position en ms
    active: true

    onPositionChanged: {
        var coord = positionSource.position.coordinate;
        
        // Les données
        console.log("Horodatage :", positionSource.position.timestamp);
        console.log("Coordonnées :", coord.longitude, coord.latitude, coord.isValid);
        console.log("Altitude :", coord.altitude, positionSource.position.altitudeValid);
        console.log("Direction :", positionSource.position.direction, positionSource.position.directionValid);
        console.log("Vitesse :", positionSource.position.speed, positionSource.position.speedValid);

        // Calcul
        console.log("Distance (Avignon) :", coord.distanceTo(QtPositioning.coordinate(43.95, 4.8167)));
    }
}

Code source : qt-android-map.zip

C++

TODO

Carte

L’API de localisation Qt est disponible pour Android.

Il faudra ajouter le module dans le fichier de projet .pro :

QT += location
  • QML :
import QtLocation 5.6
  • C++ :
#include <QGeoRoute> 

Liens :

QML

L’affichage d’une carte est effectué à l’aide du type QML Map. Le type Map prend en charge les interactions de l’utilisateur via le type QML MapGestureArea. Pour accéder aux données cartographiques qui seront affichées dans un objet Map, QML fournit un Plugin de service.

Exemple :

Window {
    id: window
    width: 512
    height: 512
    visible: true

    Plugin {
        id: mapPlugin
        locales: "fr_FR"
        name: "osm" // OpenStreetMap
        PluginParameter { name: "osm.geocoding.host"; value: "https://nominatim.openstreetmap.org" }
    }
    Map {
        plugin: mapPlugin
        center: QtPositioning.coordinate(43.95, 4.8167) // Avignon
        zoomLevel: 14
    }
}

Les cartes peuvent également contenir des objets de superposition qui sont utilisés pour afficher des informations. Il existe un ensemble d’objets de superposition prédéfinis de base : MapCircle, MapRectangle, MapPolygon, MapPolyline et MapQuickItem.

Code source : qt-android-map.zip

C++

TODO

Géocodage

Le géocodage consiste à associer des coordonnées géographiques (longitude/latitude) à une adresse postale. Qt fournit le type QML GeocodeModel pour rechercher ces information géographiques. Il permettra de connaître l’adresse postale complète à partir de ses coordonnées géographiques (longitude/latitude). Le géocodage inverse est aussi possible grâce à GeocodeModel.

Le signal onLocationsChanged indiquera une nouvelle position que l’on pourra récupérer avec la méthode get(). On disposera des informations de géolocalisation (latitude/longitude) et de l’adresse.

Button {
        onClicked: {
            // Test GeocodeModel
            geocodage.query = "République,Avignon,France"
            geocodage.update()
        }
}
    
GeocodeModel {
        id: geocodage
        plugin: mapPlugin
        onLocationsChanged: {
            if(error)
                console.log("Erreur GeocodeModel : " + error + " - " + errorString)
            if (count>=1)
            {
                console.log("Adresse : " + get(0).address.text + "\n" + get(0).coordinate)
            }
        }
 }

Le type QML RouteModel est utilisé pour extraire des itinéraires géographiques d’un fournisseur (cf. Plugin). Les itinéraires incluent des données sur les itinéraires entre deux points, des itinéraires avec plusieurs points de passage et divers autres concepts similaires. Il s’utilise avec des vues telles que MapItemView.

Il faut créer un RouteQuery avec les points de route. Si autoUpdate est activé, la mise à jour sera automatiquement effectuée. Les données stockées et renvoyées dans RouteModel sont constituées d’objets Route sous forme de liste avec le nom routeData.

Button {
        onClicked: {
            // Test RouteModel
            map.center = QtPositioning.coordinate(44.0868587, 4.96439917)
            routeQuery.clearWaypoints()
            routeQuery.addWaypoint(QtPositioning.coordinate(44.0868587, 4.96439917))
            routeQuery.addWaypoint(QtPositioning.coordinate(43.95, 4.8167))
            map.update()
        }
}

Map {
    id: map
    
    RouteModel {
        id: routeModel
        plugin: mapPlugin
        query: RouteQuery {
            id: routeQuery
            travelModes: RouteQuery.CarTravel
            routeOptimizations: RouteQuery.ShortestRoute
        }
    }

    MapItemView {
        model: routeModel
        delegate: MapRoute {
            route: routeData
            line.color: "blue"
            line.width: 5
            smooth: true
            opacity: 0.5
        }
    }
}

Dessin

Les cartes peuvent également contenir des objets de superposition qui sont utilisés pour afficher des informations. Il existe un ensemble d’objets de superposition prédéfinis de base : MapCircle, MapRectangle, MapPolygon, MapPolyline et MapQuickItem.

Tous les éléments de dessin surperposés sur la carte doivent être intégrés dans l’élément Map.

  • MapCircle : élément QML permettant d’afficher un cercle géographique sur une carte. Il peut être centré sur la carte grâce à sa propriété center. Voir aussi : MapQuickItem.
Map {
...
   MapCircle {
        id: cercle
        center: map.center
        radius: 40
        color: 'green'
        border.width: 3
        opacity: 0.25
   }
}
  • MapQuickItem : élément QML utilisé pour placer un objet sur une carte à un emplacement et avec une taille. un MapQuickItem suivra le panoramique (et éventuellement le zoom) de la carte comme s’il se trouvait sur la surface de la carte. Le positionnement est contrôlé par deux propriétés : coordinate et anchorPoint.
MapQuickItem {
    id: marker
    anchorPoint.x: image.width/4
    anchorPoint.y: image.height
    sourceItem: Image {
        id: image
        source: "images/marqueur.png"
    }
}
  • MapItemView élément QML utilisé pour remplir une Map avec des éléments d’un modèle. Même s’il n’a pas de représentation autonome, il est intégré à la carte. On l’utilise par exemple pour afficher un itinéraire sur une carte.
Map {
    RouteModel {
        id: routeModel
        plugin: mapPlugin
        query: RouteQuery {
            id: routeQuery
            travelModes: RouteQuery.CarTravel
            routeOptimizations: RouteQuery.ShortestRoute
        }
    }

    MapItemView {
        model: routeModel
        delegate: MapRoute {
            route: routeData
            line.color: "blue"
            line.width: 5
            smooth: true
            opacity: 0.5
        }
    }
}

Erreurs SSL

Il est possible d’obtenir des erreurs liées à SSL.

  • Pour un kit Desktop :

Messages d’erreur du type : qt.network.ssl QSslSocket ...

Actuellement la version de Qt ne supporte que la version 1.0 de ssl (et non la version 1.1). Pour pallier au problème, il faut installer :

$ sudo apt-get install libssl1.0-dev
  • Pour un kit Android :

Messages d’erreur liés à libcrypto.so et libssl.so.

Il faut télécharger la bibliothèque openssl et la compiler pour la cible Android. La version de Qt utilisée ici est la 5.10.1. La fabrication nécessite une installation de Qt5 pour Android fonctionnelle.

Une fois fabriquées les deux bibliothèques seront copiées dans le répertoire android/libs/arm du projet Qt. Il faudra lier l’application en l’indiquant dans le fichier .pro.

On utilisera le script Setenv-android.sh qu’il faudra paramétrer en fonction de son installation :

$ wget https://www.openssl.org/source/openssl-1.0.2q.tar.gz

$ tar zxvf openssl-1.0.2q.tar.gz

$ cd openssl-1.0.2q/

$ wget https://wiki.openssl.org/images/7/70/Setenv-android.sh

$ vim Setenv-android.sh

_ANDROID_NDK="android-ndk-r10e"
_ANDROID_EABI="arm-linux-androideabi-4.9"
_ANDROID_ARCH=arch-arm
_ANDROID_API="android-18"

ANDROID_NDK_ROOT="/.../Android/Sdk/$_ANDROID_NDK"

$ . ./Setenv-android.sh 

$ ./Configure shared android

$ make CALC_VERSIONS="SHLIB_COMPAT=; SHLIB_SOVER=" build_libs

$ mkdir -p /.../Qt_Projets/.../android/libs/arm

$ cp libcrypto.so /.../Qt_Projets/.../android/libs/arm
$ cp libssl.so /.../Qt_Projets/.../android/libs/arm

$ vim *.pro

ANDROID_EXTRA_LIBS = \
        $$PWD/android/libs/arm/libcrypto.so \
        $$PWD/android/libs/arm/libssl.so

Les deux bibliothèques sont fournies dans l’archive de l’exemple qt-android-map.zip.

Site : tvaira.free.fr