だけじゃない、Amazon Location サービス



## Amazon LocationService 　は、アーキテクチャをの組み合わせにシームレス

一昨年re:invent 2022 に参加した時に　イベントアプリに採用されてる見知らぬサービス「へ〜、こんなものが出るのか」。
というところから半年、最近このLocationServiceを利用した開発に携わることが多く多用しています。
売りとしては「GoogleMap」ではない「AWSが提供する、サードパーティのMapコンポーネント」、「GPS情報から経路予測」と　一般的な　位置情報を利用したSaasサービスとして遜色ないくらいの機能を提供しています。

提供される仕組みの居内、便利だなと思ったものが「ジオフェンス」
・ある矩形で囲んだ領域に　出入りすると、その度に　EventBridgeを発火させてくれるようになる。
今までは、スマホなどのGPSなど位置情報を持ったデバイスから定期的にデータを集め、Lambda などで矩形に対して現在位置が含まれるかなどを判定するロジックを描いていたところを　イベントトリガの仕組みとして実装しなくて良くなる。

## どんなことにつかえるのか
最近のIoTデバイスでは　家に近づいたら、遠のいたらで　エアコンを制御したり。してくれたりするのだけれど、　我が家では「キッズな携帯」にかませることで、学童からの帰りなどの通知に使っている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8216/2f165ff6-5fe5-e027-4ee5-151f93cf89a0.png)

## 「そんなサービスあるじゃん。」

なんといってもre:invent の会場を案内してくれるところがあるので試してみた結果「ひとり暮らしの後輩の部屋を半分に割って、あっちとこっちにわけたジオフェンスの矩形データ」でさえ反応してくれるのだｗ
結構細かい精度でも動作するし、何より　ライブラリ提供以外にも　PWAとして実装が可能となるので重たい仕組みを作る必要がなくなる。


## 仕組み
次の単語と関係を覚えておくとといです。

```: LocationServiceジオフェンス内の定義関係
   [🗾コレクション:領域一覧] 　　　　　　　　<----- [🔍フィルタ/ルール] ------> [🔥EventBeidge]
　　　　　[📱トラッカー: デバイス一覧] 　　　<----/
```


- コレクション：　反応させたい領域を定義したJSONオブジェクトを定義したもの　（複数設定できるのでコレクションと呼ぶのだと思います）
- トラッカー: スマホデバイスなどの通知を行いたい対象の一覧を定義する。
- フィルタルール: コレクションに対して、反応する際のルールや対象となるトラッカーを絞ることができる。

説明については、実際のLocationService開発ドキュメントを読むことをお勧めします。言い回しを変えるよりもわかりやすいです。（AWSのドキュメントに慣れない方もいるかもなので、ぜひ読んで見てのおすすめです）
https://docs.aws.amazon.com/ja_jp/location/latest/developerguide/geofence-tracker-concepts.html


## 開発する方法
1. Geo フェンスのメッシュデータを作成する
 -　[GeoJson生成サービスツール](https://geojson.io/#map=2/0/20　)を使って作れる。
 -　
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8216/7868cc39-1bcd-44d8-8f59-d728a9d9885e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8216/817062cc-61dd-4930-e78b-ccf50dcc96e6.png)


2. LocationSErvice にデータを登録する
取り込んだデータはそのまま登録ができるので　開発リソースないにJsonファイルとして管理が可能
このデータをAWSコンソールに登録する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8216/3c80718b-b76c-f8cd-973b-815cba2ccaaf.png)

部分的な記事となってしまいますがヒントとして、Serverlessフレームワークで実装する際に必要なリソース定義

```yaml: serverless.yml
    :
　  (略 : 開発リソースの準備)
    :
custom:
  config: ${file(../../Configrations/config.${sls:stage}.yml)}
  ProjectName: ${self:custom.config.ProjectName}
  ResourcePrefix: ${self:custom.ProjectName}-${sls:stage}
  
  # Lambdaの環境変数
  lambdaEnvironment:
    DYNAMODB_TABLE_NAME: ${self:custom.ResourcePrefix}-PrimaryTable
    TRACKER_NAME: ${self:custom.ResourcePrefix}-Tracker
    PUSU_NOTIFICATION_QUEUE_URL: https://sqs.${aws:region}.amazonaws.com/${aws:accountId}/${self:custom.ResourcePrefix}-PushNotificationQueue
    APP_URL: ${self:custom.config.appUrl}
    LOG_LEVEL: ${self:custom.config.lambdaLoglevel}
    STATIC_BUCKET_NAME:  ${self:custom.ResourcePrefix}-static
    :
　  (略)
    :
functions:
  HandleGeoEvent:
    name: ${self:custom.ResourcePrefix}-HandleGeoEvent
    handler: Functions/HandleGeoEvent/HandleGeoEvent.handler
    environment:
      ${self:custom.lambdaEnvironment}

resources:
  Resources:

    #--------------------------------------------------------------------------
    # 地図
    #--------------------------------------------------------------------------
    OpenDataMap:
      Type: AWS::Location::Map
       : (略：本件では利用しないため)    
    HereMap:
      Type: AWS::Location::Map
       : (略：本件では利用しないため)
       
    #--------------------------------------------------------------------------
    # Geofence
    #--------------------------------------------------------------------------

    ## [SPEC] ジオフェンスのための　コレクションインスタンス
    GeofenceCollection:
      Type: AWS::Location::GeofenceCollection
      Properties:
        CollectionName: ${self:custom.ResourcePrefix}-GeofenceCollection
    ## [SPEC] デバッグ用LogGroup
    GeofenceLogGroup:
      Type: AWS::Logs::LogGroup
      DependsOn: GeofenceCollection
      Properties:
        LogGroupName: "/aws/events/${self:custom.ResourcePrefix}-GeofenceCollection"
        RetentionInDays: 
    ## [SPEC] ジオフェンスイベントルール　（反応した際に鳥がイベントを発火してもらうための仕組み）
    GeofenceEventRule:
      Type: AWS::Events::Rule
      DependsOn: GeofenceLogGroup
      Properties:
        Description: EventBridge Rule for Amazon Location Service Geofence Events
        EventPattern:
          source:
            - aws.geo
          detail-type:
            - "Location Geofence Event"
        Name: ${self:custom.ResourcePrefix}-GeofenceEventRule
        Targets:
          -
            Id: 1
            Arn: !GetAtt GeofenceLogGroup.Arn
          -
            Id: 2
            Arn: !GetAtt HandleGeoEventLambdaFunction.Arn
    # EventBridgeからCWにログを出すためのリソースポリシー
    EventBridgeCloudwatchLogsPermission:
      Type: AWS::Logs::ResourcePolicy
          :
        (略: CWLog に出力するためのポリシ定義：本説明では主出ないので省略)
          :
    # EventBridgeからlambdaを起動するためのリソースポリシー
    EventBridgeInvokeLambdaPermission:
      Type: AWS::Lambda::Permission
      Properties:
        Action: lambda:InvokeFunction
        FunctionName: !GetAtt HandleGeoEventLambdaFunction.Arn
        Principal: events.amazonaws.com
        SourceArn: !GetAtt GeofenceEventRule.Arn

    #--------------------------------------------------------------------------
    # Trakcer
    #--------------------------------------------------------------------------
    ## ジオフェンスのコレクションに対する　トラッカー（発火処理するもの）
    Tracker:
      Type: AWS::Location::Tracker
      DependsOn: GeofenceCollection
      Properties:
        PositionFiltering: TimeBased
        TrackerName: ${self:custom.ResourcePrefix}-Tracker
```


3. スマホアプリケーションを作る

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8216/2ae93e20-205e-aee9-b452-a9fd8942b5cc.png)

大事なことは、EventBridgeのハンドリングを実装するLambda / Pinpoint などのハンドラ処理モジュール
```typescript: HandleGeoEvent
/**
 * ジオフェンスが挙げたENTER/EXITイベントに反応して通知を送信する。
 */
import { EventBridgeHandler } from 'aws-lambda';
    :
　  (略)
    :
/**
 * ハンドラ
 */
const main: EventBridgeHandler<EventBridgeEventDetailType, EventBridgeEventDetail, void> = async (event) => {
// 通知時に必要な情報する。
  const phoneId = event.detail.DeviceId;
  const contentId = event.detail.GeofenceId;

  // Geo通知はENTERイベントのみを対象とする
  if (event.detail.EventType !== 'ENTER') {
    //TODO 領域に入った場合の処理
  }
    :
　  (略) : 主にPUSH を自己表示させるための情報を準備する
    :
// PUSH通知送信
  const notificationRequest: PushNotificationRequest = {
    id: ulid(),                    // 通知情報をユニークに管理する場合UUIDなどを付与すると便利
    phoneId: event.detail.DeviceId,// ★★スマホなどのデバイスを特定するID (通知サービスに合わせて実装)
    type: 'GEO',                   // [独自] 通知がジオフェンス反応の物かを記載
    contentId: content.id,         // ★★コンテントID （通知に必要なID）
    imgName: `${content.id}.jpg`,  // [独自] 通知に利用したい画像
    title: content.title,          // 通知タイトル
    body: content.leadText,　　　　　　　　　　　　　　　// 通知本文
  };
  await pushNotificationUtil.requestPushNotificationAync(notificationRequest);
  logger.info(`プッシュ通知予約完了 id:${notificationRequest.id}`);
};
  
```



4. 通知が来る(EventBridge が発火し、「指定領域の出入りのイベント」「位置情報」「時間」　を送ってくれる)
5. Lambda などで処理を実装する（ここから　SNSを使って通知しても良いし、Pinpointと組み合わせるのもあり）
　　まず簡素に実装する場合は　Dynamoにデータを登録しておくことがおすすめ。 
　　　　　　

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8216/48845ddb-a7ef-b6d5-f096-fa470b2ef089.png)

## 知っておくと便利な豆知識：　（いいこともわるいこともあるけど　使ってみた際に注意すべきことを記載しておきます：対策付き）
1. GeoMap の一覧を　CRUD 管理できない
    - 一度Geoデータを作成するが　、登録したデータはTrackingするために　登録をするが、　登録したデータ自体は、編集したりどんなデータを登録していたかを今の画面で見ることができない。
    - ツールがまだない状態の場合は、WikiやExcelなどで登録データの管理を別途行なっておくことをお勧めします。
        - 次の段階でできるのがShellを作っておくこと。ただ、SPAを書いてしまいAPIで登録するのが最も簡単です。
    ※（ごめんなさい）　SPA: SinglePageApplicationにクレデンシャルを埋め込むと　APIを呼び出してデータ登録してくれるツールをOSS公開しようとしたのですが（後輩と作成したので利権的に生理中とさせてくダサい。このアプリケーションは、内容は単純で、SPAの下に置いたGEoMapのデータをフォルダで管理し、CRUDするというもの。こういったツールを用意しておくと便利。） 

2. 画面では直接　Geofenceのポリゴンを編集することができない
AWS コンソール上からは　TrackingとGeoFencingとMapの管理はできるが、実際のMapを表示させてGeoの矩形データを作るようなエディタはない。
    - → ただし、開発ドキュメントを開くと、Editorが世の中に存在するから　取り込みを行うことは可能
    
    - ※参照：　こちらのはなしは　AWS のLocationServiceのページに載っていました。（エディタをOSSにしようと作ってます）
- 精度や反応速度は使ってみて悪くない
    - 利用時通信環境
    - 例えば、後輩の家の一間を半分に割って　ジオフェンスしても反応するくらいの精度はある。
- 実際の現地テストをするには
    - デバッグ機能があるわけではないので、実際のジオマップの範囲をテストしようとすると　クライアントアプリケーションの緯度経度を偽造して送信する必要がある
        -アプリケーションを偽装してインストールすると誤魔化せるアプリがあるが、あまりうまくいかなかった。
            - https://www.tenorshare.jp/change-gps/recommend-app-to-change-the-mobile-phone-location.html
        　→ 対策としては、試験環境向けの座標マップと実際のマップは分けて開発した方がいい。
         
:::note warn


現地テストは大事！
:::

- 緯度経度が　「経度・緯度」　、作成するデータと実際に触るデータと　おや？と思うことがあったんですが「あべこべになってたので」実装時設計時は注意が必要。


## まとめ：　Cost Aware Architectures Implement Cost Controls <by Frugal Architect #5>: コスト思考のアーキテクチャはコストを制御できる。

１つのアプリケーションを設計しようとする時、複数の機能になるリソースを重要度や必要に応じたアーキテクチャとして利用して設計することが可能になる。（引用）

- 複雑な仕組みをAWSの他のリソースとシームレスに組み合わせることができる。　例えば、ソーシャル連携をする仕組みと思って触り始めたCognitoは、認証をすると認可の部分で、認証可否ごとのIAMロールを払い出してくれるので、「ログインできない」ではなく「ロールに利用したいサービスリソースの権限ポリシーがある・ない」　となり「クレデンシャルを埋め込む仕組みがなくなる」。
- 同じくして「わざわざ無駄なI/Fを作ることなく、AWSの提供する機能と簡潔に結合させ組み込むことが容易」になる。
便利な仕組みを理解し、選択肢としてのLocationServiceリソースを選ぶことで　様々なアイデアのアプリケーションを０から作ることなく開発ができることに感動すると思います。

是非、「モダンアーキテクチャ」を取り込むための第一歩「全て自分で作らない」努力というトライをしてみるはいかがでしょうか。

## 参考：

### THE FRUGAL ARCHITECT: 
- re:invent2023 で　AWSのCTO werner vogels aws　が　「Frugal Architect」についてKeynoteを発表していた　モダンアーキテクチャを構築するための指針７つ。
- リンク：　https://thefrugalarchitect.com/

### 参考: https://aws.amazon.com/jp/location/
### LocationService を利用するのに役立ったページ 参考: LocationService の開発者ガイドページ https://docs.aws.amazon.com/ja_jp/location/latest/developerguide/welcome.html