ジオロケーション・トラッキングでUber/Lyftアプリを作ろう

PubNub Developer Relations - Feb 22 - - Dev Community

アーカイブからのこの投稿では、車両の位置を追跡するためにPubNubを使用して、独自のUberまたはLyftクローンを構築する方法を説明します。 コード・スニペットのいくつかは古いバージョンのライブラリを参照しているかもしれませんが、この記事にあるものはすべて今日の読者にとって適切なものです。

他のデバイスのリアルタイムの位置を追跡することは、特にLyftやUberのような人気のあるモバイルアプリの基本です。

LyftやUberのように。この機能を構築するのは難しく、面倒なものだが、PubNubを利用した運転手と乗客の間の簡単なパブリッシュ/サブスクライブ・モデルで簡素化しよう。

チュートリアル概要

このチュートリアルの全ソースコードはこちらから入手できます。

このチュートリアルでは、この一般的なユースケースをAndroidアプリケーションで実装する方法を紹介します。今回作成するサンプルアプリでは、まずユーザーに運転手か同乗者かを尋ねます。ユーザーがドライバーの場合、現在地はチャンネルに公開され、5秒ごと(このサンプルでは)、またはアプリが必要とする回数ごとに更新されます。

ユーザーが乗客の場合は、同じチャンネルにサブスクライブして、ドライバーの現在位置の更新を受け取ります。このパブリッシュ/サブスクライブモデルは、下のビジュアルに表示されています。

Uber/Lyft App

Google Maps APIとPubNubのセットアップ

まず、地図機能のためにPubNub Android SDKとGoogle Play Servicesを使用するために、Gradleの依存関係を記述する必要があります。また、PubNubメッセージの基本的なJSON解析を完了するために、

com.fasterxml.jackson
Enter fullscreen mode Exit fullscreen mode

ライブラリを 実装します。

dependenciesの下にあるアプリモジュールのbuild.gradleファイルに、以下のコマンドのcodeblockを入力し、必要なものがあることを確認する。メニューのツール部分にあるAndroid SDK ManagerからGoogle Play Servicesがインストールされていることを確認し、必要なライブラリを実装する。

implementation group: 'com.pubnub', name: 'pubnub-gson', version: '4.12.0'
implementation 'com.google.android.gms:play-services-maps:15.0.1'
implementation "com.google.android.gms:play-services-location:15.0.1"
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.9.2'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.9.2'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.2'
Enter fullscreen mode Exit fullscreen mode

必要な依存関係が揃ったので、Android Manifestファイルに必要なパーミッションがあることを確認しよう。ネットワーク状態へのアクセスとインターネット接続の確立のパーミッションがあれば、ユーザーはPubNub APIへの接続を行うことができる。 位置情報パーミッションは後でドライバーの位置情報を要求するときに使用する。

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
Enter fullscreen mode Exit fullscreen mode

AndroidアプリでPubNubを設定するには、PubNub Admin DashboardでPubNubアプリを作成する必要があります (無料です)。 アプリを作成すると、購読キーと公開キーが割り当てられます。 次のパートでは、これらの認証情報をMainActivityクラスで使用して接続を確立します。

次に、Google Maps APIキーを設定し、アプリケーションで使用できるようにします。これを行うには、Google の Developer API コンソールにアクセスします。ここで新しいプロジェクトを作成し、APIキーを生成します。APIキーが手に入ったので、次のコードを書いてGoogle Maps APIを設定し、生成したAPIキーで設定します。

<meta-data
   android:name="com.google.android.gms.version"
   android:value="@integer/google_play_services_version" />
<meta-data
   android:name="com.google.android.geo.API_KEY"
   android:value=ENTER_KEY_HERE" />
Enter fullscreen mode Exit fullscreen mode

メインアクティビティ

MainActivityクラスには3つの重要な機能があります:

  1. 運転手か乗客かに応じて、ユーザーをそれぞれのインターフェースに誘導する。
  2. アプリのすべてのユーザーに対してPubNubへの接続を確立する。
  3. 位置情報へのアクセス許可のチェック

まず、ユーザーをドライバーと乗客に分け、それぞれのユーザーインターフェースを提供します。これはDriverActivityクラスとPassengerActivityクラスを作成することで行います。詳細は第2回と第3回で説明します。

MainActivityでは、ユーザーに運転手または乗客としての役割をアプリに知らせるよう促す、シンプルなボタンベースのインターフェースを作成します。AndroidのIntentクラスを使って、どのボタンが押されたかに応じて、MainActivityクラスからDriverActivityクラスへ、またはMainActivityクラスからPassengerActivityクラスへ遷移することができます。

インターフェイスは次のように表示されます:

screenshot

driverButton.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
       startActivity(new Intent(MainActivity.this, DriverActivity.class));
   }
});
passengerButton.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
       startActivity(new Intent(MainActivity.this, PassengerActivity.class));
   }
});
Enter fullscreen mode Exit fullscreen mode

次に、Admin DashboardからPubNubの認証情報を使用して、

PNConfiguration
Enter fullscreen mode Exit fullscreen mode

インスタンスを 作成 します。これにより 、アプリとPubNubを接続する

PubNub
Enter fullscreen mode Exit fullscreen mode

インスタンスを 作成 できます。MainActivityクラスのこのPubNubインスタンスは、DriverActivityとPassengerActivityの両方からアクセスできるようにpublicかつstaticに します。アプリのすべてのユーザーに対してPubNubが適切に初期化されるように、Main Activityの

onCreate()
Enter fullscreen mode Exit fullscreen mode

メソッドで以下のメソッドを呼び出します 。これはすべて次のコードに示されています。

private void initPubnub() {
        PNConfiguration pnConfiguration = new PNConfiguration();
        pnConfiguration.setSubscribeKey(Constants.PUBNUB_SUBSCRIBE_KEY);
        pnConfiguration.setPublishKey(Constants.PUBNUB_PUBLISH_KEY);
        pnConfiguration.setSecure(true);
        pubnub = new PubNub(pnConfiguration);
    }
Enter fullscreen mode Exit fullscreen mode

最後に、ユーザーがアプリの位置情報サービスを有効にしていることを確認します。これは次のコードで実行できます。もしユーザーが位置情報へのアクセスを許可していない場合、アプリはこの許可を要求します。

public void checkPermission() {
   if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
           ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
           ) {//Can add more as per requirement
       ActivityCompat.requestPermissions(this,
               new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION},
               123);
   }
}
Enter fullscreen mode Exit fullscreen mode

ドライバーのアクティビティ

DriverActivityクラスの唯一の機能は、ドライバーの位置を取得し、一定の時間間隔でこのデータをPubNubチャネルに公開することです。

まず、位置情報のリクエストを行う方法を見てみましょう。 ドライバーに位置情報の更新を要求するために、Google Location APIの

FusedLocationProviderClient
Enter fullscreen mode Exit fullscreen mode

。また、

LocationRequest
Enter fullscreen mode Exit fullscreen mode

クラスを使用して、優先度、最小変位、時間間隔など、位置情報リクエストの重要なパラメータを指定 します。

このサンプルでは、時間間隔を5000ミリ秒にして、5秒ごとに位置情報を更新するようにし、最小変位を10メートルにして、少なくとも10メートルの変化があった場合にのみ位置情報を更新するようにする。最後に、高精度の位置追跡のために、モバイル・デバイスのGPSを使用することを希望する。この高精度は、乗客が目的地まで地図を横切って移動するドライバー(車のアイコンで表示される)の詳細な表示を見ることを可能にするために重要である。

LocationRequest locationRequest = LocationRequest.create();
locationRequest.setInterval(5000);
locationRequest.setFastestInterval(5000);
locationRequest.setSmallestDisplacement(10);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
Enter fullscreen mode Exit fullscreen mode

位置情報リクエストのパラメータを定義したので、この LocationRequest オブジェクトを FusedLocationProviderClient オブジェクトの位置情報更新リクエストに渡します。

mFusedLocationClient.requestLocationUpdates(locationRequest, new LocationCallback() {
   @Override
   public void onLocationResult(LocationResult locationResult) {
       Location location = locationResult.getLastLocation();
...
Enter fullscreen mode Exit fullscreen mode

リクエストから得た位置情報をLinkedHashMapに変換し、PubNubチャネルの位置情報メッセージのフォーマットに合うようにします。LinkedHashMapには2つのキー "lat "と "lng "と、これらのキーに対応する値があり、どちらもStringになります。

これで、LinkedHashMap形式のメッセージをPubNubチャンネルに公開することができるようになりました。これを次のコードに示す。メインアクティビティのonCreateメソッドでPubNubインスタンスを取得するために

MainActivity.pubnub
Enter fullscreen mode Exit fullscreen mode

。PubNubを再度初期化して複数の接続を確立することは避けたい。代わりに、MainActivityクラスですでに作成したものを使用します。

MainActivity.pubnub.publish()
       .message(message)
       .channel(Constants.PUBNUB_CHANNEL_NAME)
       .async(new PNCallback<PNPublishResult>() {
           @Override
           public void onResponse(PNPublishResult result, PNStatus status) {
               // handle publish result, status always present, result if successful
               // status.isError() to see if error happened
               if (!status.isError()) {
                   System.out.println("pub timetoken: " + result.getTimetoken());
               }
               System.out.println("pub status code: " + status.getStatusCode());
           }
       });
Enter fullscreen mode Exit fullscreen mode

パッセンジャー・アクティビティ

PassengerActivityクラスで、更新中のドライバーの位置を表示するMapViewを作成します。Passenger ActivityにMapView UI要素を追加するには、このアクティビティに対応するlayout.xmlファイルに次のコードを含める必要があります。

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/map"
   android:name="com.google.android.gms.maps.SupportMapFragment"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context="com.example.mapwithmarker.MapsMarkerActivity" />
Enter fullscreen mode Exit fullscreen mode

次に、レイアウトファイルで定義したIDを使用して 、

MapFragment
Enter fullscreen mode Exit fullscreen mode

オブジェクトを インスタンス化 します。サンプルコードでは、IDとして「map」を使用しています。また 、PassengerActivityに

onMapReadyCallback
Enter fullscreen mode Exit fullscreen mode

を実装して、

onMapReady
Enter fullscreen mode Exit fullscreen mode

メソッドを設定します。そして、

PassengerActivity.this
Enter fullscreen mode Exit fullscreen mode

をMapFragmentオブジェクトの

getMapAsync
Enter fullscreen mode Exit fullscreen mode

メソッドに パラメータとして 渡します 。これで、MapFragmentオブジェクトのonMapReadyコールバックメソッドをセットアップすることができる。

mMapFragment = (SupportMapFragment) getSupportFragmentManager()
       .findFragmentById(R.id.map);
mMapFragment.getMapAsync(PassengerActivity.this);
Enter fullscreen mode Exit fullscreen mode

さて、ドライバーの現在位置の更新を受け取るために、ドライバーの位置チャンネルを購読したい。マップの準備ができたら、このコードをコールバックメソッドに含める。さらに、

SubscribeCallback
Enter fullscreen mode Exit fullscreen mode

を持つリスナーを追加して、メッセージを受信したときにアプリがどのように動作するかを知って おく必要がある。

PubNubのAndroidドキュメントに記載されているように、まずリスナーを追加し、次にPubNubインスタンスをチャネルにサブスクライブするという順序で実行する必要があります。メッセージを受信したら、JSON出力をLinkedHashMapに変換し、簡単にパースして経度と緯度を取得できるようにしたい。JSON出力をLinkedHashMap に変換す るには、

JsonUtil
Enter fullscreen mode Exit fullscreen mode

クラスをインポー� �する必要がある。 クラスには 、これを可能に するメソッド

fromJson()
Enter fullscreen mode Exit fullscreen mode

が含まれて いる。このクラスは全ソースコードに示されている。ドライバーの位置がわかったら、メソッド

updateUI()
Enter fullscreen mode Exit fullscreen mode

を呼び出し、新しい位置をパラメータとして渡す ことで、UIを更新しなければならない 。

MainActivity.pubnub.addListener(new SubscribeCallback() {
    @Override
    public void status(PubNub pub, PNStatus status) {
    }
    @Override
    public void message(PubNub pub, final PNMessageResult message) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                try {
                    Map<String, String> newLocation = JsonUtil.fromJson(message.getMessage().toString(), LinkedHashMap.class);
                    updateUI(newLocation);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
    @Override
    public void presence(PubNub pub, PNPresenceEventResult presence) {
    }
});
MainActivity.pubnub.subscribe()
        .channels(Arrays.asList(Constants.PUBNUB_CHANNEL_NAME)) // subscribe to channels
        .execute();
Enter fullscreen mode Exit fullscreen mode

updateUIメソッドには2つのケースがあります。

最初のケースは、ドライバーのマーカーがまだ存在しない場合です。この場合、

Marker
Enter fullscreen mode Exit fullscreen mode

オブジェクトを インスタンス化 し、その位置をドライバーの最初に追跡された位置に設定 します。また、ドライバーの車のストリートビューを維持するために、地図を拡大しなければならない。

つ目のケースは、すでにドライバーのマーカーが存在する場合です。この場合、Markerオブジェクトをインスタンス化し直す必要はありません。この場合、Markerオブジェクトを再度インスタンス化する必要はなく、単純にマップ上のマーカーの位置を新しい場所に移動するだけです。これは

animateCar()
Enter fullscreen mode Exit fullscreen mode

メソッドを呼び出すことで行います。また、MapViewのカメラが新しい場所に移動することを確認する必要があります。従って、ドライバーのマーカーは常にMapViewの範囲内にあることになります。これを次のコードに示す。

private void updateUI(Map<String, String> newLoc) {
        LatLng newLocation = new LatLng(Double.valueOf(newLoc.get("lat")), Double.valueOf(newLoc.get("lng")));
        if (driverMarker != null) {
            animateCar(newLocation);
            boolean contains = mGoogleMap.getProjection()
                    .getVisibleRegion()
                    .latLngBounds
                    .contains(newLocation);
            if (!contains) {
                mGoogleMap.moveCamera(CameraUpdateFactory.newLatLng(newLocation));
            }
        } else {
            mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
                    newLocation, 15.5f));
            driverMarker = mGoogleMap.addMarker(new MarkerOptions().position(newLocation).
                    icon(BitmapDescriptorFactory.fromResource(R.drawable.car)));
        }
    }
Enter fullscreen mode Exit fullscreen mode

animateCarメソッドでは、車のマーカーを滑らかな線で新しい場所に移動させます。このアニメーションは5秒間続けなければなりません。なぜなら、このアプリは5秒ごとにドライバーの位置の更新を受け取るからです。こうすることで、アニメーションが終わるたびに、次のアニメーションを開始するための新しい位置情報が更新される。そうすることで、車のアニメーションのタイムラグを最小限に抑えることができる。

また

LatLngInterpolator
Enter fullscreen mode Exit fullscreen mode

インターフェースも利用する。これにより

interpolate()
Enter fullscreen mode Exit fullscreen mode

メソッドを実装することができる。この端数はメソッド

getAnimatedFraction()
Enter fullscreen mode Exit fullscreen mode

.インスタンスからこのメソッドを呼び出します。

ValueAnimator
Enter fullscreen mode Exit fullscreen mode

インスタンスからこのメソッドを呼び出します。

onAnimationUpdate()
Enter fullscreen mode Exit fullscreen mode

コールバックメソッドから得られるインスタンスからこのメソッドを呼び出します。この分数は一定の割合で増加し、5秒後には1になる(新しい場所までずっと)。この車のアニメーションは、以下のコードに示されている。

private void animateCar(final LatLng destination) {
        final LatLng startPosition = driverMarker.getPosition();
        final LatLng endPosition = new LatLng(destination.latitude, destination.longitude);
        final LatLngInterpolator latLngInterpolator = new LatLngInterpolator.LinearFixed();
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
        valueAnimator.setDuration(5000); // duration 5 seconds
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                try {
                    float v = animation.getAnimatedFraction();
                    LatLng newPosition = latLngInterpolator.interpolate(v, startPosition, endPosition);
                    driverMarker.setPosition(newPosition);
                } catch (Exception ex) {
                }
            }
        });
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
            }
        });
        valueAnimator.start();
    }
Enter fullscreen mode Exit fullscreen mode
private interface LatLngInterpolator {
       LatLng interpolate(float fraction, LatLng a, LatLng b);
       class LinearFixed implements LatLngInterpolator {
           @Override
           public LatLng interpolate(float fraction, LatLng a, LatLng b) {
               double lat = (b.latitude - a.latitude) * fraction + a.latitude;
               double lngDelta = b.longitude - a.longitude;
               if (Math.abs(lngDelta) > 180) {
                   lngDelta -= Math.signum(lngDelta) * 360;
               }
               double lng = lngDelta * fraction + a.longitude;
               return new LatLng(lat, lng);
           }
       }
   }
Enter fullscreen mode Exit fullscreen mode

さらなる改良

このサンプルをさらに発展させたいのであれば、1つの機能として、車が曲がるときにマーカーも曲がるようにするとよいでしょう。この機能を追加するには、メッセージ構造にキーと値のペアを1つ追加する必要があります。"bearing "というキーと値を入れる必要があります。そうすれば、ドライバーの方位によってマーカーを回転させるだけだ。

おめでとう!

これで、MainActivity、DriverActivity、PassengerActivityを作成し、シンプルなLyft/Uber Androidデモを作成するのに必要なPublish/Subscribeモデルを構築することに成功しました。完全なソースコードはこちらをクリックしてください。

埋め込みコンテンツがこのページで利用できない場合は、https://www.youtube.com/embed/kQ6EzqoQu5Aからもご覧いただけます。

その他のリソース

PubNubはどのようにあなたを助けることができますか?

この記事はPubNub.comに掲載されたものです。

PubNubのプラットフォームは、開発者がWebアプリ、モバイルアプリ、IoTデバイス向けにリアルタイムのインタラクティブ機能を構築、提供、管理できるように支援します。

私たちのプラットフォームの基盤は、業界最大かつ最もスケーラブルなリアルタイムエッジメッセージングネットワークです。世界15か所以上で8億人の月間アクティブユーザーをサポートし、99.999%の信頼性を誇るため、停電や同時実行数の制限、トラフィックの急増による遅延の問題を心配する必要はありません。

PubNubを体験

ライブツアーをチェックして、5分以内にすべてのPubNub搭載アプリの背後にある本質的な概念を理解する

セットアップ

PubNubアカウントにサインアップすると、PubNubキーに無料ですぐにアクセスできます。

始める

PubNubのドキュメントは、ユースケースやSDKに関係なく、あなたを立ち上げ、実行することができます。

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .