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 / Xcode9 / 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" />

※ACCESS_COARSE_LOCATION はGPSが使えない屋内やビルの谷間などでもネットワークを利用して大まかな情報を得ます。
※ACCESS_FINE_LOCATION はGPSデバイスを使用して精度の高い位置情報を取得します。


(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>NSLocationWhenInUseUsageDescription</key>
<string>位置情報を使用します。</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>

※上記の設定では、後述のコードでバックグラウンドで動作させるために、UIBackgroundModes の設定を追加しています。これが無いと、AllowsBackgroundUpdates = true でエラーとなりますので、注意が必要です。


(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の精度に指定
}

/// <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
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からご覧になれます。
http://itblogdsi.blog.fc2.com/blog-entry-81.html



関連記事

コメント

コメントの投稿

カテゴリ別記事一覧

広告

プロフィール

石河 純


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

IT関連の知識はざっくりとこんな感じです。
【OS関連】
WindowsServer: 2012/2008R2/2003/2000/NT4
Windows: 10/8/7/XP/2000/me/NT4/98
Linux: CentOS RedHatLinux9
Mac: macOS Sierra 10.12 / OSX Lion 10.7.5 / OSX Snow Leopard 10.6.8
【言語・データベース】
VB.net ASP.NET C#.net Java VBA
Xamarin.Forms
Oracle10g SQLServer2008R2 SQLAnywhere8/11/16
ActiveReport CrystalReport ReportNet(IBM)
【ネットワーク関連】
CCNP シスコ技術者認定
Cisco Catalyst シリーズ
Yamaha RTXシリーズ
FireWall関連
【WEB関連】
SEO SEM CSS IIS6/7 apache2

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