Delphiでお手軽プログラミング

Delphiでお手軽プログラミングメニュー

緯度・経度・高度・移動速度・移動方向を取得するAndroidアプリケーションを作成する

~TLocationSensorコンポーネントでは緯度・経度しか取得できないので高度・移動速度・移動方向も取得する


1.Delphiを起動

Delphiを起動して新規FMXプロジェクトを作成し、ターゲットプラットフォームをAndroidに設定して、 フォームにTMemoを1つ配置します。
プロジェクトとユニットを保存します。



2.権限設定

おおよその位置情報へのアクセス、詳細な位置情報へのアクセスが必要です。


3.ソースコードの記述

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics,
  FMX.Dialogs, FMX.Controls.Presentation, FMX.ScrollBox,
  FMX.Memo,FMX.StdCtrls, System.DateUtils,

  Androidapi.JNI.Location,
  Androidapi.JNIBridge,
  Androidapi.JNI.JavaTypes,
  Androidapi.JNI.Os,
  Androidapi.Helpers,
  Androidapi.JNI.GraphicsContentViewText,
  Androidapi.Jni.Support,
  FMX.DialogService,
  //Android8(ApiLevel26)以降で権限許可の為に必要なユニット
  System.Permissions ;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { private 宣言 }
    LocationManager : JLocationManager;
    LocationListener : JLocationListener;
    procedure onLocationChanged(location: JLocation);
    //Android8(ApiLevel26)以降対応関数
    procedure PermissionRequestResult(Sender:TObject; const APermissions:TArray<string>;
      const AGrantResults: TArray<TPermissionStatus>);
    //Android8(ApiLevel26)以降対応関数
    procedure RequestPermissions();
  public
    { public 宣言 }
  end;

  TOnLocationChanged=procedure(location:JLocation) of object;

  TMyLocationListener = class(TJavaLocal, JLocationListener)
  private
    Folc:TOnLocationChanged;
  public
    constructor Create(olc:TOnLocationChanged);
    procedure onLocationChanged(location: JLocation); cdecl;
    procedure onProviderDisabled(provider: JString); cdecl;
    procedure onProviderEnabled(provider: JString); cdecl;
    procedure onStatusChanged(provider: JString; status: Integer; extras: JBundle); cdecl;
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

{ TMyLocationListener }
constructor TMyLocationListener.Create(olc: TOnLocationChanged);
begin
  inherited Create();
  Folc:=olc;
end;

procedure TMyLocationListener.onLocationChanged(location: JLocation);
begin
  if Assigned(Folc) then Folc(location);
end;

procedure TMyLocationListener.onProviderDisabled(provider: JString);
begin
end;

procedure TMyLocationListener.onProviderEnabled(provider: JString);
begin
end;

procedure TMyLocationListener.onStatusChanged(provider: JString;
  status: Integer; extras: JBundle);
begin
end;

{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
var LocationService: JObject;
    location : JLocation;
begin
  //Android8(ApiLevel26)以降はアプリから権限要求が必要
  RequestPermissions;

  LocationService :=
    TAndroidHelper.Context.getSystemService(
      TJContext.JavaClass.LOCATION_SERVICE
    );
  LocationManager :=
    TJLocationManager.Wrap(
      (LocationService as ILocalObject).GetObjectID
    );
  LocationListener:=
    TMyLocationListener.Create(self.onLocationChanged);

  //位置情報が変化したら通知をもらう
  LocationManager.requestLocationUpdates(
    TJLocationManager.JavaClass.GPS_PROVIDER,
    5000,//最小間隔5秒で
    10,  //距離10m以上移動したら通知
    locationListener,
    TJLooper.JavaClass.getMainLooper
  );

  //過去に取得した位置情報で最後の情報を取得する
  location:=LocationManager.getLastKnownLocation(
    TJLocationManager.JavaClass.GPS_PROVIDER
  );
  //とりあえず、それを出力
  if assigned(location) then
    self.onLocationChanged(location);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  //位置情報通知の解除
  LocationManager.removeUpdates(LocationListener);
end;

procedure TForm1.onLocationChanged(location: JLocation);
var dt:TDateTime;
begin
  dt:=UnixToDateTime(location.getTime div 1000,false);
  Memo1.Lines.Add('日時:'+FormatDateTime('yyyy/mm/dd hh:nn:ss',dt));
  Memo1.Lines.Add('緯度:'+location.getLatitude.ToString+'度');
  Memo1.Lines.Add('経度:'+location.getLongitude.ToString+'度');
  Memo1.Lines.Add('誤差半径:'+location.getAccuracy.ToString+'m');

  //WGS84準拠高度から「ジオイド高」を引くと標高が求められる
  //ジオイド高は国土地理院サイトで緯度・経度から求めることが出来る
  Memo1.Lines.Add('WGS84準拠高度:'+location.getAltitude.ToString+'m');
  Memo1.Lines.Add('高度誤差:±'+location.getVerticalAccuracyMeters.ToString);

  Memo1.Lines.Add('移動速度:'+location.getSpeed.ToString+'m/s');
  Memo1.Lines.Add('移動方向:'+location.getBearing.ToString+'度(北が0で時計回)');
  Memo1.Lines.Add('');
  Memo1.GoToTextEnd;
end;

procedure TForm1.PermissionRequestResult(Sender:TObject;
  const APermissions:TArray<string>; const AGrantResults: TArray<TPermissionStatus>);
begin
  //FineLocationの権限があるか
  if (AGrantResults[0]<>TPermissionStatus.Granted) then
  begin  //権限がない場合
    //「□今後は表示しない」チェックボックスにチェックが入っているか
    if (TJActivityCompat.JavaClass.shouldShowRequestPermissionRationale(
         TAndroidHelper.Activity,
         TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE)) then
    begin
      //「□今後は表示しない」チェックボックスにチェックが入っていない場合
      // 非同期でダイアログを表示して説明と許可を要求
      TDialogService.MessageDialog(
        '許可しないとアプリが動作しません。',TMsgDlgType.mtInformation,
        [TMsgDlgBtn.mbOK],TMsgDlgBtn.mbOk,0,
        procedure (const AResult: TModalResult)
        begin
          //2回目以降は「□今後は表示しない」チェックボックスが表示される
          RequestPermissions();
        end);
    end
    else
    begin
      //「□今後は表示しない」チェックボックスにチェックが入っている
      TDialogService.MessageDialog(
        '権限が無いため終了します。「設定⇒アプリと通知」から権限設定してください。',
        TMsgDlgType.mtError,[TMsgDlgBtn.mbOK],TMsgDlgBtn.mbOK,0,
        procedure(const AResult:TModalResult)
        begin
          Application.Terminate;
        end
      );
    end;
  end;
end;

procedure TForm1.RequestPermissions;
begin
  //位置情報サービスの権限要求を行う
  PermissionsService.RequestPermissions(
    [JStringToString(TJManifest_permission.JavaClass.ACCESS_FINE_LOCATION)],
    PermissionRequestResult
  );
end;

end.


4.実行する

507SH(Android One) Android8.1に、32Bitコンパイルしてインストールして実行

Pixel 3a XL Android11に、64Bitコンパイルしてインストールして実行

5.注意点

移動方位はスマホの向きではなく、移動した方位を示します。

表示される高度は、WGS84準拠の高度で標高ではありません。
標高に変換するには[ジオイド高]を引く必要があるそうです。

[標高] ≒ [WGS84準拠の高度]-[ジオイド高]

ジオイド高は国土地理院サイト(https://fgd.gsi.go.jp/download/geoid.php)から、登録してログインするとダウンロードできます。
国土地理院のジオイド計算サイト(https://vldb.gsi.go.jp/sokuchi/surveycalc/geoid/calcgh/calc_f.html)で緯度経度を入力しても計算できます。


Copyright 2021 Mam