前回の続きになります。

まずは大きな壁となっていた『Xam.Plugin.Geolocator』で現在地が取得できない問題ですが、試行錯誤の結果、解決に至りました。

前回の記事に追記していってもよかったのですが、パッケージ統合の経緯にも繋がる話なので、(迷惑ですが)記念に残しておこうと思います。

ということで、今回はうまくいかなかった現在地の取得から、GoogleMap上のライン表示までご紹介していきます。

現在地の取得問題

発生した現象

これまで下記のコードを使用して現在地の取得を行ってきました。

// 現在地点の座標取得
IGeolocator locater = CrossGeolocator.Current;
Plugin.Geolocator.Abstractions.Position geoCurrentPosition = await locater.GetPositionAsync();

しかし上記の非同期メソッド実行時に応答がなく、タイムアウトまでは待ちませんでしたが、結果として取得できませんでした。

現象について調べてみた

まずはパッケージのGitHubサイトを確認してみました。

注目すべきは『The Future: Xamarin.Essentials』の部分です。

和訳はGoogle先生に任せるとして、要約すると「将来的には『Xamarin.Essentials』に統合される」となります。

対処方法

ということで、『Xam.Plugin.Geolocator』ではなく、『Xamarin.Essentials』を使用して現在地の取得を行うことにしました。

次なる壁

ところが、いざインストール、というところでプロジェクトが依存関係の最小バージョンを満たしていないことが判明しました。

迷わずAndroidのターゲットを『Android 8.1』に、次いで全Nugetパッケージも更新したところ、下記のエラーが発生。

ざっと(と言っても検索結果は2件だけ)調べてみたところ、『Xamarin.Build.Download』というパッケージの最新化で解消した事例を発見。

依存関係に含まれていないパッケージのインストールで改善、という点に違和感は感じつつもインストールしてみました。

しかし、エラー解消せず

最終的に

結論から言うと、私の場合は『VisualStudioの更新』で何とかエラーが消えました。

原因が不明なままですが、ひとまず開発環境も含めてすべて最新化することをオススメします。

GoogleMap上のライン表示

ということで、原因不明なまま何とかエラーを解消し、前回分のピン表示まで確認できたので、いよいよ本題に入っていこうと思います。

※現在地取得の詳細は後ほど。

今回は以下の環境で実装を行いました。

<Project Sdk="Microsoft.NET.Sdk">

 ~略~

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
    <PackageReference Include="Xamarin.Build.Download" Version="0.4.11" />
    <PackageReference Include="Xamarin.Essentials" Version="1.1.0" />
    <PackageReference Include="Xamarin.Forms" Version="4.0.0.482894" />
    <PackageReference Include="Prism.Unity.Forms" Version="7.1.0.431" />
    <PackageReference Include="Xamarin.Forms.GoogleMaps" Version="3.2.0" />
    <PackageReference Include="Xamarin.Forms.GoogleMaps.Bindings" Version="2.2.0" />
  </ItemGroup>

</Project>

View

GoogleMap上にラインを表示するには『Polyline』というクラスを使用します。

MVVMでの実装を行うために、Viewには『BindingPolylinesBehavior』を追加します。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:maps="clr-namespace:Xamarin.Forms.GoogleMaps;assembly=Xamarin.Forms.GoogleMaps"
             xmlns:bindings="clr-namespace:Xamarin.Forms.GoogleMaps.Bindings;assembly=Xamarin.Forms.GoogleMaps.Bindings"
             x:Class="GoogleMapsSample.Views.MainPage"
             Title="{Binding Title}">

    <maps:Map>
        <maps:Map.Behaviors>
            <bindings:MoveToRegionBehavior Request="{Binding MoveToRegionRequest}"/>
            <bindings:BindingPinsBehavior Value="{Binding Pins}"/>
            <bindings:BindingPolylinesBehavior Value="{Binding Polylines}"/>
        </maps:Map.Behaviors>
    </maps:Map>

</ContentPage>

Activity

Xamarin.Essentials』使用にあたり、初期化が必要になります。

using Android.App;
using Android.Content.PM;
using Android.OS;
using Prism;
using Prism.Ioc;

namespace GoogleMapsSample.Droid
{
    [Activity(Label = "GoogleMapsSample", Icon = "@mipmap/ic_launcher", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);

            // 追加したコード
            Xamarin.FormsGoogleMaps.Init(this, bundle);
            Xamarin.FormsGoogleMapsBindings.Init();
            Xamarin.Essentials.Platform.Init(this, bundle);

            LoadApplication(new App(new AndroidInitializer()));
        }
    }

    public class AndroidInitializer : IPlatformInitializer
    {
        public void RegisterTypes(IContainerRegistry container)
        {
            // Register any platform specific implementations
        }
    }
}

ViewModel

現在地の取得も含めて、コードを見て頂いた方が早いかと…。

using Prism.Commands;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Net.Http;
using System.Threading.Tasks;
using Xamarin.Essentials;
using Xamarin.Forms.GoogleMaps;
using Xamarin.Forms.GoogleMaps.Bindings;

namespace GoogleMapsSample.ViewModels
{
    public class MainPageViewModel : ViewModelBase
    {
        // 表示位置移動用
        public MoveToRegionRequest MoveToRegionRequest { get; } = new MoveToRegionRequest();

        // ライン
        public ObservableCollection<Polyline> Polylines { get; set; }

        // ピン
        public ObservableCollection<Pin> Pins { get; set; }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="navigationService"></param>
        public MainPageViewModel(INavigationService navigationService)
            : base(navigationService)
        {
            Title = "Main Page";

            DispRouteCommand = new DelegateCommand(async () => await DispRoute());
        }

        /// <summary>
        /// 画面遷移時処理
        /// </summary>
        /// <param name="parameters"></param>
        public override async void OnNavigatedTo(INavigationParameters parameters)
        {
            base.OnNavigatedTo(parameters);

            // 画面遷移後に現在位置へ移動する
            await MoveToCurrentLocation();
        }

        /// <summary>
        /// 現在位置へ移動するメソッド
        /// </summary>
        public async Task MoveToCurrentLocation()
        {
            // 現在地点の座標取得
            Location currentLocation = await Geolocation.GetLocationAsync();

            // PositionのConvert
            Position xamCurrentPosition =
                new Position(currentLocation.Latitude, currentLocation.Longitude);

            // 表示範囲を現在位置へ移動
            MoveToRegionRequest.MoveToRegion(new MapSpan(xamCurrentPosition, 0.3d, 0.3d));

            // 現在地点にピンを打つ
            Pin pin = new Pin();
            pin.Label = "現在地点";
            pin.Position = xamCurrentPosition;
            Pins.Add(pin);

            // 現在地からのラインを引く地点
            Position targetPosition = new Position(xamCurrentPosition.Latitude - 0.1, xamCurrentPosition.Longitude - 0.1);

            // 現在地からちょっとのところまでラインを引く
            Polyline polyline = new Polyline();
            polyline.Positions.Add(xamCurrentPosition);
            polyline.Positions.Add(targetPosition);
            Polylines.Add(polyline);

            // 分かりやすいようにラインの終端にピンを打つ
            pin = new Pin();
            pin.Label = "終着点";
            pin.Position = targetPosition;
            Pins.Add(pin);
        }
    }
}

現在地の取得を行っているのは下記のコードです。

// 現在地点の座標取得 
Location currentLocation = await Geolocation.GetLocationAsync();

Xam.Plugin.Geolocator』を使用していた時よりも、可読性に優れましたね。

実装としては、ラインを引く地点を『Polyline』オブジェクトに設定し、バインドするだけです。

実行結果がこちら。

2地点間にラインを引くことが出来ました。

次回予告

次回は今回行ったライン表示を活用していきます。

以上です。

スポンサーリンク