Créer une application Windows utilisant un GPS et Bing Maps

Dans le billet précédent, je vous montrai comment utiliser Bing Maps et l’API de géolocalisation des navigateurs Web. Maintenant, je vous propose d’utiliser un GPS connecter en Bluetooth à votre ordinateur et d’afficher votre position dans une application Windows écrite avec Delphi (ici j’utiliserai la version 7 Édition Personnel disponible gratuitement sur le site de Développez.com).

Voici comment procéder :

Récupération de la clé permettant d’utiliser l’API Bing Maps

Pour pouvoir utiliser l’API de Bing Maps, il vous faut d’abord une clé permettant de vous connecter aux serveurs de Microsoft. Vous pouvez la créer gratuitement (pour une utilisation non commercialle limitée à 50 000 requêtes par jour) sur le site Bing Maps Account Center. Cliquez sur le lien « Create or view keys » sur la gauche après vous êtes connecté avec un compte Windows Live.

Installation des composants nécessaires à la création du programme

Pour pouvoir créer votre application, il vous faut tous d’abord récupérer 2 composants (voir 3 si vous êtes avec la version Personnel de Delphi 😦 ) :

  1. ComPort, pour la gestion des ports COM. Étant donné que Windows émule un port COM pour la connexion Bluetooth au GPS.
  2. TGPS, un composant que j’ai crée pour la gestion des informations envoyées par le GPS.
  3. Indy pour Delphi 7 (miroir), la version Personnel ne possédant pas ces composants, il vous faut les installer. Le site suivant vous donne la marche à suivre pour effectuer l’installation : Borland Delphi 7 et Composant Indy.

Installation de ComPort

Pour installer le composant ComPort, il vous faut extraire le contenu du fichier Zip que vous venez de télécharger dans un dossier que vous créerez dans le répertoire de Delphi. Par exemple comport. Puis rendez-vous dans le répertoire Source et lancez le fichier DsgnCPort7.dpk puis cliquez sur Compiler, puis après quelques secondes, cliquez sur Installer.

Faites de même avec le fichier CPortLib7.dpk. Puis une fois les 2 paquets installés, allez dans le menu Outils/Options d'environnement…, dans la boîte de dialogue qui s’ouvre allez dans l’onglet Bibliothèque et cliquez sur les trois petits points sous Chemin de bibliothèque. Dans la fenêtre qui s’ouvre, ajoutez le chemin de votre répertoire Source.

C’est bon, ComPort est installé sous Delphi.

Installation de TGPS

La procédure est sensiblement la même, sauf qu’avec Delphi 7 Édition Personnel vous aurez une erreur de compilation lors de l’installation. Pour corriger ça, il vous faudra modifier un peu le code pour supprimer le composant qui pose problème. Tous d’abord, retirez les fichiers GPX.pas et xmlrtl.dcp du paquet.

Puis double-cliquez sur GPS.pas pour l’éditer. Dans les uses supprimez la référence à GPX :

uses
  SysUtils, Classes, Windows, CPortTypes, CPort, Math, StrUtils, ConvUtils,
  StdConvs, DateUtils, Forms, StdCtrls, Controls, Graphics, Imglist, Messages,
  GPX;

Repérez puis supprimez la déclaration de la classe TGPStoGPX :

  TGPStoGPX = class (TComponent)
  private
    FActive: Boolean;
    FGPS: TCustomGPS;
    FGPSLink: TGPSLink;
    FFileName: String;
    FGPXFile: IXMLGpxType;
    FGPXTrack: IXMLTrkType;
    FGPXSeg: IXMLTrksegType;
    LastFixTime: TDateTime;
    procedure SetActive(const Value: Boolean);
    procedure SetGPS(const Value: TCustomGPS);
    procedure SetFileName(const Value: String);
    procedure GPSChange(Sender: TObject; GPSDatas: TGPSDatas);
  protected
    procedure Notification(AComponent: TComponent; Operation: TOperation);
      override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy(); override;
    procedure Connect();
    procedure Disconnect();
  published
    property Active: Boolean read FActive write SetActive;
    property GPS: TCustomGPS read FGPS write SetGPS;
    property FileName: String read FFileName write SetFileName;
  end;

Puis supprimez les lignes de la classe TGPStoGPX :

constructor TGPStoGPX.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  LastFixTime := 0;
  FGPSLink := TGPSLink.Create();
  FGPSLink.OnGPSDatasChange := Nil;
  FGPSLink.OnGPSDatasChange := GPSChange;
end;

destructor TGPStoGPX.Destroy();
begin
  if Active then
    Disconnect();
  GPS := Nil;
  FGPSLink.Free();
  inherited Destroy();
end;

procedure TGPStoGPX.Connect();
begin
  if not FileExists(FFileName) then
  begin
    FGPXFile := Newgpx();
    with FGPXFile do
    begin
      Version := '1.1';
      if Application.Title <> '' then
        Creator := Application.Title
      else
        Creator := ExtractFileName(Application.ExeName);
      with Metadata do
      begin
        Name := FFileName;
        Author.Name := 'Mesotech';
        Author.Email.Id := 'contact';
        Author.Email.Domain := 'mesotech.fr';
        Author.Link.Text := 'Site Web de Mesotech';
        Author.Link.Href := 'http://www.mesotech.eu/';
      end;
    end;
  end
  else
    FGPXFile := Loadgpx(FFileName);

  FGPXTrack := FGPXFile.Trk.Add();
  FGPXTrack.Name := 'Track: ' + DateTimeToStr(Now());

  FGPXSeg := FGPXTrack.Trkseg.Add();

  FActive := True;
end;

procedure TGPStoGPX.Disconnect();
var
  XMLFile: TextFile;
begin
  if Assigned(FGPXFile) then
  begin
    AssignFile(XMLFile, FFileName);
    Rewrite(XMLFile);
    Write(XMLFile, FGPXFile.XML);
    CloseFile(XMLFile);
  end;

  FActive := False;
end;

procedure TGPStoGPX.SetActive(const Value: Boolean);
begin
  if not ((csDesigning in ComponentState) or (csLoading in
    ComponentState)) then
  begin
    if Value <> FActive then
      if Value then
        Connect()
      else
        Disconnect();
  end
  else
    FActive := Value;
end;

procedure TGPStoGPX.SetGPS(const Value: TCustomGPS);
begin
  if FGPS <> Value then
  begin
    if Assigned(FGPS) then
      FGPS.UnRegisterLink(FGPSLink);
    FGPS := Value;
    if Assigned(FGPS) then
    begin
      FGPS.FreeNotification(Self);
      FGPS.RegisterLink(FGPSLink);
    end;
  end;
end;

procedure TGPStoGPX.SetFileName(const Value: String);
begin
  if Active then
    Disconnect();
  FFileName := Value;
end;

procedure TGPStoGPX.GPSChange(Sender: TObject; GPSDatas: TGPSDatas);
var
  ID: Integer;
  OldLat, OldLon: Double;
  FormatDate: TFormatSettings;
  GPSTime: TDateTime;
  TZICurrent: TTimeZoneInformation;
begin
  if Active and GPSDatas.Valid then
  begin
    GetLocaleFormatSettings($0409, FormatDate);
    FormatDate.ShortDateFormat := 'yyyy-mm-dd"T"hh:nn:ss"Z"';
    ID := FGPXSeg.Trkpt.Count;
    if ID > 0 then
      with FGPXSeg.Trkpt.Items[ID - 1] do
      begin
        OldLat := StrToFloat(Lat, FormatDate);
        OldLon := StrToFloat(Lon, FormatDate);
      end
    else
    begin
      OldLat := 0;
      OldLon := 0;
    end;

    if not (SameValue(GPSDatas.Latitude, OldLat, 0.00001) or
      SameValue(GPSDatas.Longitude, OldLon, 0.00001)) and not
      (IsZero(GPSDatas.Latitude) or IsZero(GPSDatas.Longitude)) then
    begin
      GetTimeZoneInformation(TZICurrent);

      GPSTime := Date() + GPSDatas.UTCTime + TZICurrent.Bias;

      if (SecondsBetween(GPSTime, LastFixTime) > SEC_BETWEEN_SEG) and
        (LastFixTime > 0) then
      begin
        FGPXSeg := FGPXTrack.Trkseg.Add();
        FGPXTrack.Name := 'Track: ' + DateTimeToStr(Now());
      end;

      with FGPXSeg.Trkpt.Add() do
      begin
        Ele := FloatToStr(GPSDatas.HeightAboveSea, FormatDate);
        Time := DateToStr(GPSTime, FormatDate);
        Lat := FloatToStrF(GPSDatas.Latitude, ffFixed, 8, 6, FormatDate);
        Lon := FloatToStrF(GPSDatas.Longitude, ffFixed, 8, 6, FormatDate);
        Sat := GPSDatas.NbrSatsUsed;
        AddChild('speed').NodeValue := GPSDatas.Speed;

        LastFixTime := GPSTime;
      end;
    end;
  end;
end;

procedure TGPStoGPX.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (AComponent = FGPS) and (Operation = opRemove) then
    GPS := Nil;
end;

Pour finir la modification, supprimez l’enregistrement du composant :

procedure Register();
begin
  RegisterComponents('GPS',
    [TGPS, TGPStoGPX, TGPSSpeed, TGPSSatellitesPosition,
    TGPSSatellitesReception, TGPSCompass]);
end;

Enregistrez le tout, compilez, installez, ajoutez le chemin des bibliothèques à Delphi et voilà…

Installation d’Indy

Même procédure que pour ComPort, sauf qu’ici il s’agit d’un auto-extractible, il suffit de l’extraire dans le bon dossier :

Compiler puis installer le paquet Indy70.dpk, puis ajouter le chemin des bibliothèques et c’est fini pour l’installation des composants.

Création de notre projet

On commence par ce créer une fiche où l’on va placer tous les composants nécessaires à notre programme :

Et maintenant le code…

J’ajoute les constantes et variables globales :

const
  // Placez ici la clé récupérée sur le site Bing Maps Account Center
  Clef = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
  Serveur = 'http://dev.virtualearth.net/REST/v1/';
  Imagerie = Serveur + 'Imagery/Map/%s/%s,%s/%d?ms=%d,%d&pp=%s,%s;6&key='
    + Clef;

var
  FBingMapsGPS: TFBingMapsGPS;
  Latitude, Longitude: Double;

Le code pour le TComComboBox permettant de paramétrer le port COM à utiliser :

procedure TFBingMapsGPS.CCMB_PortChange(Sender: TObject);
begin
  // Change le port utilisé par le GPS
  GPS1.Port := CCMB_Port.Text;
end;

Le code pour le bouton démarrant le GPS (un TSpeedButton avec la propriété GroupIndex à 1) :

procedure TFBingMapsGPS.BTN_DemarrerGPSClick(Sender: TObject);
begin
  try
    // Démarre le GPS si le bouton est enfoncé
    GPS1.Connected := BTN_DemarrerGPS.Down;

    // Démarre le rafraîchisement automatique de la carte
    TIM_BingMaps.Enabled := BTN_DemarrerGPS.Down;
  except
    on E: Exception do
    begin
      // Remonte le bouton
      BTN_DemarrerGPS.Down := False;

      // Affiche un message d'erreur
      MessageDlg('Erreur lors du démarrage du GPS.'#13#10
        + E.ClassName + ' : ' + E.Message, mtError, [mbOk], 0);
    end;
  end;

  // Change le texte du bouton
  case BTN_DemarrerGPS.Down of
    True :
      BTN_DemarrerGPS.Caption := '&Démarrer le GPS';
    False :
      BTN_DemarrerGPS.Caption := '&Arrêter le GPS';
  end;
end;

Le code pour l’exécution du GPS :

procedure TFBingMapsGPS.GPS1GPSDatasChange(Sender: TObject;
  GPSDatas: TGPSDatas);
begin
  with GPSDatas do
  begin
    // Change le contenu des TLabel pour avoir la position GPS
    LBL_Latitude.Caption  := Format('Latitude : %2.6f°', [Latitude]);
    LBL_Longitude.Caption := Format('Longitude : %2.6f°', [Longitude]);
    LBL_Altitude.Caption  := Format('Altitude : %f m', [HeightAboveSea]);

    // Coche la case si la position captée est valide
    CHK_Valide.Checked    := Valid;
  end;
end;

Le code pour rafraîchir la carte toute les secondes (utilisant les unités Math et JPEG) :

procedure TFBingMapsGPS.TIM_BingMapsTimer(Sender: TObject);

  // Retourne le répertoire temporaire de Windows
  function RepertoireTemporaire(): String;
  var
    lpBuffer: array[0..255] of Char;
  begin
    GetTempPath(SizeOf(lpBuffer), lpBuffer);
    Result := lpBuffer;
  end;

  // Transforme un nombre réel en chaîne
  function ReelVersStr(Nombre: Double): String;
  var
    FrmNmb: TFormatSettings;
  begin
    try
      // -> http://msdn.microsoft.com/library/0h88fahh
      GetLocaleFormatSettings($0409, FrmNmb);
      Result := FloatToStr(Nombre, FrmNmb);
    except
      Result := '0';
    end;
  end;

var
  Adresse, Carte, LatStr, LongStr, TypeVue: String;
  Reponse: TFileStream;
begin
begin
  // Si le GPS est connecté
  if GPS1.Connected then
  begin
    // Si la position a changée (attention : utilise l'unité Math)
    if not SameValue(GPS1.GPSDatas.Latitude, Latitude, 0.0001) and
      not SameValue(GPS1.GPSDatas.Longitude, Longitude, 0.0001) then
    begin
      // Récupère la position
      Latitude  := GPS1.GPSDatas.Latitude;
      Longitude := GPS1.GPSDatas.Longitude;

      // Converti la position en texte
      LatStr    := ReelVersStr(Latitude);
      LongStr   := ReelVersStr(Longitude);

      // Si la vue satellite est cochée
      if CHK_Satellite.Checked then
      begin
        TypeVue := 'AerialWithLabels';
      end
      else
      begin
        TypeVue := 'Road';
      end;

      // Ne pas oublier d'ajouter l'unité JPEG
      Carte   := RepertoireTemporaire() + 'Carte.jpeg';

      // Prépare l'adresse de l'image à charger
      Adresse := Format(Imagerie, [TypeVue, LatStr, LongStr,
        TB_Zoom.Position, IMG_Carte.Width, IMG_Carte.Height, LatStr, LongStr]);

      // Charge l'image à partir de Bing Maps
      Reponse := TFileStream.Create(Carte, fmCreate);
      try
        HTTP_BingMaps.Get(Adresse, Reponse);
      finally
        Reponse.Free();
      end;
      IMG_Carte.Picture.LoadFromFile(Carte);
    end;
  end;
end;

Maintenant rafraîchir aussi la carte lorsque le zoom ou la vue satellite et changé.

procedure TFBingMapsGPS.CHK_SatelliteClick(Sender: TObject);
begin
  // Force le rafraîchissement de la carte
  Latitude  := 0;
  Longitude := 0;
  TIM_BingMapsTimer(TIM_BingMaps);
end;

procedure TFBingMapsGPS.TB_ZoomChange(Sender: TObject);
begin
  // Force le rafraîchissement de la carte
  Latitude  := 0;
  Longitude := 0;
  TIM_BingMapsTimer(TIM_BingMaps);
end;

Fermeture du GPS à la fermeture de l’application:

procedure TFBingMapsGPS.FormDestroy(Sender: TObject);
begin
  // Arrête le GPS à la fermeture de l'application
  GPS1.Close();
end;

Si jamais l’utilisateur clique sur la case à cocher « Position valide », on prévoit le coup :

procedure TFBingMapsGPS.CHK_ValideClick(Sender: TObject);
begin
  // Coche la case uniquement si le signal est valide
  CHK_Valide.Checked := GPS1.GPSDatas.Valid;
end;

Voilà, l’écriture du code est terminée. Il ne vous reste plus qu’à compiler et à tester.

Vous pouvez télécharger le code source sur GitHub à l’adresse :
GitHub https://github.com/ILPlais/BingMapsGPS

Publicités

~ par ILP sur 24 novembre 2012.

Une Réponse to “Créer une application Windows utilisant un GPS et Bing Maps”

  1. Bonjour! Il y a quelques années, je suis arrivé sur ce site.
    http://gr66.free.fr/delphi/prog1.html
    pour pouvoir lire les données dans un GPS. Avec votre application, il y a TGPSToGPX, m’a émerveillé pour ré-plonger dans ce que je n’ai pas pu terminé il y a 10 ans. Merci

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

 
%d blogueurs aiment cette page :