fc2ブログ

記事一覧

GPS 機能を実装して位置情報を取得する方法 | Xamarin.Forms


今回は Xamarin.Forms で GPS を使用して位置情報を取得する方法についてご紹介いたします。
あまりネイティブコードを書かない様に NuGet パッケージの Xam.Plugin.Geolocator を採用しました。


前提条件
・Windows10 Pro 64Bit 1709
・Visual Studio 2015 Community Update3
・Xamarin 4.8.0.760 (NuGet Xamarin.Forms 2.4.0.282)
・macOS Sierra 10.12.6 / Xcode 9 / Xamarin.iOS 11.6.1.4



1.Xam.Plugin.Geolocator

まずは NuGet パッケージマネージャから Geolocator で検索してインストールします。
現在の最新のバージョンでは .Net Standard に準拠していますので、私の場合は PCL で実装していますので、v3.0.4 を選択して、Android プロジェクトと iOS プロジェクトにインストールします。PCL にインストールは不要です。

xamarin_gps_01.png



2.PCLの記述方法

(1)PCL で位置情報の受け渡しができるように ViewModel クラスを配置します。

GeoCoordinate.cs
namespace AppName.ViewModels
{
    /// <summary>
    /// 位置情報格納クラス
    /// </summary>
    [System.Diagnostics.DebuggerStepThrough()]
    public class GeoCoordinate
    {
        /// <summary>
        /// 緯度
        /// </summary>
        public double Latitude { get; set; }

        /// <summary>
        /// 経度
        /// </summary>
        public double Longitude { get; set; }
       
        /// <summary>
        /// 高度
        /// </summary>
        public double Altitude { get; set; }

        /// <summary>
        /// 取得時間
        /// </summary>
        public DateTimeOffset Timestamp { get; set; }       
    }
}


(2)次に PCL プロジェクト内に DependencyService で呼び出すためのインターフェースを配置します。

ILocationService.cs
using System.Threading.Tasks;
using AppName.ViewModels;
namespace AppName.Services
{
public delegate void OnLocationChangedDelegate(GeoCoordinate location);
public delegate void OnLocationErrorDelegate(string error);

//DependencyServiceから利用する
public interface ILocationService
{
// GPS初期化処理
void Initialize();

//現在の位置情報取得(非同期)
Task<GeoCoordinate> GetGeoCoordinateAsync(int timeout);

// 非同期で位置情報の変更をリスニングする
void StartListening(int minTime, double minDistance, bool includeHeading = false);

//位置情報変更イベント
event OnLocationChangedDelegate OnLocationChanged;
event OnLocationErrorDelegate OnLocationError;
}
}



3.Androidの実装方法

(1)AndroidManifest.xml に以下のパーミッションを追加します。

AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-feature android:name="android.hardware.location.gps"
android:required="false" />
<uses-feature android:name="android.hardware.location.network"
android:required="false" />
<!-- ForegroundServiceを実装している場合 -->
<service android:name="MyForeGroundService"
android:foregroundServiceType="location" >
</service>

※ACCESS_COARSE_LOCATION は GPS が使えない屋内やビルの谷間などでもネットワークを利用して大まかな情報を得ます。
※ACCESS_FINE_LOCATION は GPS デバイスを使用して精度の高い位置情報を取得します。
※ACCESS_BACKGROUND_LOCATION は バックグラウンドで GPS デバイスを使用して位置情報を取得する場合に必要です。Android Q (API level 29)で新たに追加されたパーミッションです。


(2)次に Android プロジェクト内に LocationService クラスを配置します。

LocationService.cs
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
using AppName.Droid.Services;
using AppName.Services;
using AppName.ViewModels;
using Plugin.Geolocator;
using Plugin.Geolocator.Abstractions;
[assembly: Dependency(typeof(LocationService))]
namespace AppName.Droid.Services
{
public class LocationService : ILocationService
{
private IGeolocator _locator = null;

/// <summary>
/// GPSを初期化する
/// </summary>
public void Initialize()
{
_locator = CrossGeolocator.Current;
_locator.DesiredAccuracy = 30; //30mの精度に指定
}

/// <summary>
/// 非同期で現在座標を取得する
/// </summary>
/// <param name="timeout">タイムアウト時間</param>
/// <returns>現在の座標</returns>
public async Task<GeoCoordinate> GetGeoCoordinateAsync(int timeout)
{
//指定した秒数までを限度に現在の位置を取得する
Position position = await _locator.GetPositionAsync(timeoutMilliseconds: timeout);

return this.ConvertGeoCoordinate(position);
}

/// <summary>
/// Plugin.Geolocator.Abstractions.Position を GeoCoordinate に変換し値を返す
/// </summary>
/// <param name="position">Plugin.Geolocator.Abstractions.Position</param>
/// <returns>GeoCoordinate</returns>
private GeoCoordinate ConvertGeoCoordinate(Position position)
{
var result = new GeoCoordinate
{
Latitude = position.Latitude, //緯度
Longitude = position.Longitude, //経度
Altitude = position.Altitude, //高度
Timestamp = position.Timestamp //取得時間
};

return result;
}

/// <summary>
/// 非同期で位置情報の変更をリスニングする
/// </summary>
/// <param name="minTime"></param>
/// <param name="minDistance"></param>
/// <param name="includeHeading"></param>
public void StartListening(int minTime, double minDistance, bool includeHeading = false)
{
if (_locator != null &&
_locator.IsGeolocationEnabled &&
_locator.IsGeolocationAvailable)
{
_locator.AllowsBackgroundUpdates = true;
_locator.StartListeningAsync(minTime, minDistance, includeHeading);

//位置変更時イベント
_locator.PositionChanged += (sender, e) =>
{
if (this.OnLocationChanged != null)
{
this.OnLocationChanged(this.ConvertGeoCoordinate(e.Position));
}
};

//位置取得エラー時イベント
_locator.PositionError += (sender, e) =>
{
if (this.OnLocationError != null)
{
this.OnLocationError(e.Error.ToString());
}
};
}
}

//位置情報変更イベント
public event OnLocationChangedDelegate OnLocationChanged;
public event OnLocationErrorDelegate OnLocationError;
}
}




4.iOSの実装方法

(1)Info.plist ファイルに以下のタグを追加します。

Info.plist
  <key>NSLocationAlwaysUsageDescription</key>
<string>位置情報を使用します。</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>位置情報を使用します。</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>位置情報を使用します。</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>

※上記の設定では、後述のコードでバックグラウンドで動作させるために、UIBackgroundModes の設定を追加しています。これが無いと、AllowsBackgroundUpdates = true でエラーとなりますので、注意が必要です。
※また、iOS11以降の仕様で、Always のみの選択肢は禁止されているようです。なので Always を求めたい場合でも NSLocationAlwaysAndWhenInUseUsageDescription のキーを設定して、GPS を「常に許可」または、「このAppの使用中のみ許可」をユーザーに選択させなければなりません。


(2)次に iOS プロジェクト内に LocationService クラスを配置します。
※ Android で実装しているコードと全く同じです。

LocationService.cs
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
using AppName.iOS.Services;
using AppName.Services;
using AppName.ViewModels;
using Plugin.Geolocator;
using Plugin.Geolocator.Abstractions;
[assembly: Dependency(typeof(LocationService))]
namespace AppName.iOS.Services
{
public class LocationService : ILocationService
{
private IGeolocator _locator = null;

/// <summary>
/// GPSを初期化する
/// </summary>
public void Initialize()
{
_locator = CrossGeolocator.Current;
_locator.DesiredAccuracy = 30; //30mの精度に指定

if (CLLocationManager.Status != CLAuthorizationStatus.AuthorizedAlways)
{
this.RequestPermision();
}
}

void RequestPermision()
{
CLLocationManager manager = new CLLocationManager();
// iOS 8 has additional permissions requirements
if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
manager.RequestAlwaysAuthorization();
}
}

/// <summary>
/// 非同期で現在座標を取得する
/// </summary>
/// <param name="timeout">タイムアウト時間</param>
/// <returns>現在の座標</returns>
public async Task<GeoCoordinate> GetGeoCoordinateAsync(int timeout)
{
//指定した秒数までを限度に現在の位置を取得する
Position position = await _locator.GetPositionAsync(timeoutMilliseconds: timeout);

return this.ConvertGeoCoordinate(position);
}

/// <summary>
/// Plugin.Geolocator.Abstractions.Position を GeoCoordinate に変換し値を返す
/// </summary>
/// <param name="position">Plugin.Geolocator.Abstractions.Position</param>
/// <returns>GeoCoordinate</returns>
private GeoCoordinate ConvertGeoCoordinate(Position position)
{
var result = new GeoCoordinate
{
Latitude = position.Latitude, //緯度
Longitude = position.Longitude, //経度
Altitude = position.Altitude, //高度
Timestamp = position.Timestamp //取得時間
};

return result;
}

/// <summary>
/// 非同期で位置情報の変更をリスニングする
/// </summary>
/// <param name="minTime"></param>
/// <param name="minDistance"></param>
/// <param name="includeHeading"></param>
public void StartListening(int minTime, double minDistance, bool includeHeading = false)
{
if (_locator != null &&
_locator.IsGeolocationEnabled &&
_locator.IsGeolocationAvailable)
{
_locator.AllowsBackgroundUpdates = true;
_locator.StartListeningAsync(minTime, minDistance, includeHeading);

//位置変更時イベント
_locator.PositionChanged += (sender, e) =>
{
if (this.OnLocationChanged != null)
{
this.OnLocationChanged(this.ConvertGeoCoordinate(e.Position));
}
};

//位置取得エラー時イベント
_locator.PositionError += (sender, e) =>
{
if (this.OnLocationError != null)
{
this.OnLocationError(e.Error.ToString());
}
};
}
}

//位置情報変更イベント
public event OnLocationChangedDelegate OnLocationChanged;
public event OnLocationErrorDelegate OnLocationError;
}
}




5.使用方法

PCL プロジェクトの中の任意のページに記述します。

TestPage.xaml.cs
using AppName.Services;
using Xamarin.Forms;
public class TestPage : ContentPage
{
void OnEnableGpsClick()
{
    //GPSデバイスを初期化する
DependencyService.Get<ILocationService>().Initialize();
//10秒までを限度に現在の位置を取得する
DependencyService.Get<ILocationService>().GetGeoCoordinateAsync(10000);

//DependencyServiceの位置変更イベントと紐づける
DependencyService.Get<ILocationService>().OnLocationChanged += new OnLocationChangedDelegate(this.LocationChanged);
DependencyService.Get<ILocationService>().OnLocationError += new OnLocationErrorDelegate(this.LocationError);

//非同期スレッドを開始する
DependencyService.Get<ILocationService>().StartListening(10, 10, true);
}

//現在位置が変更されたら通知する
private void LocationChanged(GeoCoordinate location)
{
DependencyService.Get<INotificationService>().SetNotifyCondition(DateTimeOffset.Now, 0, "");
DependencyService.Get<INotificationService>().On("位置変更", "LocationUtility", "緯度:" + location.Latitude.ToString() + " 経度:" + location.Longitude + " 高度:" + location.Altitude);
}

//現在位置の取得エラーがあった場合、通知する
private void LocationError(string error)
{
DependencyService.Get<INotificationService>().SetNotifyCondition(DateTimeOffset.Now, 0, "");
DependencyService.Get<INotificationService>().On("LocationUtility", "位置取得エラー", error);
}
}

※今回の位置情報変更イベントでは NotificationService を使用して画面に通知しています。詳しくは以前の記事「ローカル通知する方法」をご覧ください。






最後までお読みいただきありがとうございます。
当ブログの内容をまとめた Xamarin逆引きメニュー は以下のURLからご覧になれます。
https://itblog.dynaspo.com/blog-entry-81.html



関連記事

コメント

コメントの投稿

※名前とタイトルが入力されていないコメントでは他のコメントとの区別ができません。

 入力されていないコメントには返信しませんのであらかじめご了承くださいませ。

※ニックネームでも良いので必ずご入力ください。

    

※必ずご入力ください。

    
    

※必ずご入力ください。

※技術的な質問には環境やエラーについて正確かつ詳細にお教えください。

・正確なエラーの内容

・Windowsのバージョン番号

・Visual Studioのバージョン

・機器の型番

・アプリやソフトのバージョン

    

カテゴリ別記事一覧

広告

プロフィール

石河 純


著者名 :石河 純
自己紹介:素人上がりのIT技術者。趣味は卓球・車・ボウリング

IT関連の知識はざっくりとこんな感じです。
【OS関連】
WindowsServer: 2012/2008R2/2003/2000/NT4
Windows: 10/8/7/XP/2000/me/NT4/98
Linux: CentOS RedHatLinux9
Mac: macOS Catalina 10.15 / Mojave 10.14 / High Sierra 10.13 / Sierra 10.12 / OSX Lion 10.7.5 / OSX Snow Leopard 10.6.8
【言語】
VB.net ASP.NET C#.net Java VBA
Xamarin.Forms
【データベース】
Oracle 10g/9i
SQLServer 2016/2008R2/2005/2000
SQLAnywhere 16/11/8
【BI/レポートツール】
Cognos ReportNet (IBM)
Microsoft PowerBI
ActiveReport (GrapeCity)
CrystalReport
【OCX関連】
GrapeCity InputMan SPREAD MultiRow GridView
【ネットワーク関連】
CCNP シスコ技術者認定
Cisco Catalyst シリーズ
Yamaha RTXシリーズ
FireWall関連
【WEB関連】
SEO SEM CSS jQuery IIS6/7 apache2

休みの日は卓球をやっています。
現在、卓球用品通販ショップは休業中です。