【Flutter】GoogleMapで、ピンのタップ判定を自作した話 (iOS編)

# 背景

以下の記事の続きです。

https://qiita.com/taku_kw/items/520ceb40121ff4d6e93d

iOSだと、正常に動作しない事が発覚しました。
ピンをタップしても反応しません。

なので、iOSでも動作させる事が出来ないか？と試行錯誤した話です。

# 本題

## First Stage

### 現象の調査

まず、画面をタップした時、どの座標をタップした判定になっているのか？を調べました。
すると、期待とは全く違う座標をタップした判定になっていました。

これにより、ピンをタップしても反応しない事に合点がいきました。

### 検証

では、前記事における、どの処理で座標のズレが発生したのか？を見ていきました。

まず見たのは、
[次の問題(5) ピンの経緯度を、地図中央を原点としたGoogleMap上の座標系(pixel単位)に変換する](https://qiita.com/taku_kw/items/520ceb40121ff4d6e93d#%E6%AC%A1%E3%81%AE%E5%95%8F%E9%A1%8C5-%E3%83%94%E3%83%B3%E3%81%AE%E7%B5%8C%E7%B7%AF%E5%BA%A6%E3%82%92%E5%9C%B0%E5%9B%B3%E4%B8%AD%E5%A4%AE%E3%82%92%E5%8E%9F%E7%82%B9%E3%81%A8%E3%81%97%E3%81%9Fgooglemap%E4%B8%8A%E3%81%AE%E5%BA%A7%E6%A8%99%E7%B3%BBpixel%E5%8D%98%E4%BD%8D%E3%81%AB%E5%A4%89%E6%8F%9B%E3%81%99%E3%82%8B)
における、 **1[pixel]あたりの経緯度** でした。
(座標のズレに直結する所なので。)

### 修正

それで、
[次の問題(3) 経緯度を、地図中央を原点としたGoogleMap上の座標系(pixel単位)に変換する](https://qiita.com/taku_kw/items/520ceb40121ff4d6e93d#%E6%AC%A1%E3%81%AE%E5%95%8F%E9%A1%8C3-%E7%B5%8C%E7%B7%AF%E5%BA%A6%E3%82%92%E5%9C%B0%E5%9B%B3%E4%B8%AD%E5%A4%AE%E3%82%92%E5%8E%9F%E7%82%B9%E3%81%A8%E3%81%97%E3%81%9Fgooglemap%E4%B8%8A%E3%81%AE%E5%BA%A7%E6%A8%99%E7%B3%BBpixel%E5%8D%98%E4%BD%8D%E3%81%AB%E5%A4%89%E6%8F%9B%E3%81%99%E3%82%8B)
に従い、iOSでの **1[pixel]あたりの経緯度** を算出しました。以下の通りです。

```dart:iOSにおける1[pixel]あたりの経緯度
ZoomLevel=1における1[pixel]あたりの緯度 = -0.596166093 
ZoomLevel=1における1[pixel]あたりの経度 =  0.703125 
```

### 効果確認

画面中央に位置するピンに限り、タップすると反応しました。
しかし、画面中央以外に位置するピンは、タップしても反応しませんでした。

## Second Stage

### 現象の調査

先の [効果確認](#-効果確認) の通りです。

### 検証

画面中央**付近**のピンではどうなるか？と考え、試してみました。
すると、期待する範囲とはやや異なるものの、反応する事が確認できました。
しかし、中央から一定以上離れたピンだと、反応しませんでした。

この事から、**中央から離れるにつれ、ピンの(x,y)座標のズレが大きくなるのでは？**という仮説を立てました。

そこで、「期待するピンの(x,y)座標」と「実際のピンの(x,y)座標が、期待からどれ程ズレているか？」を、グラフにプロットしてみました。

#### x方向

横軸が「期待するピンのx座標」
縦軸が「実際のピンのx座標の、期待からのズレ具合」
![x方向.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/421601/20bb0da6-6989-4026-5903-cd4b73c0be7d.png)

#### y方向

横軸が「期待するピンのy座標」
縦軸が「実際のピンのy座標の、期待からのズレ具合」
![y方向.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/421601/9a0d9892-4dbf-4ce9-8d32-e7664aafe5bb.png)

見ての通り、1次関数的にズレが大きくなっていく事が分かりました。

### 修正

先の[検証](#-検証)のグラフでは、「期待するピンの(x,y)座標」が分かっていないといけません。
しかし、プログラムは、実行中にその事は分かりません。
そこで、「実際のピンの(x,y)座標」に変えて、再度グラフにプロットしました。

#### x方向

横軸が「実際のピンのx座標」
縦軸が「実際のピンのx座標の、期待からのズレ具合」
![x方向2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/421601/266cf955-dca5-5a4e-0afe-c8e7e9d4ccd0.png)

#### y方向

横軸が「実際のピンのy座標」
縦軸が「実際のピンのy座標の、期待からのズレ具合」
![y方向2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/421601/169030b1-d70b-82d6-c923-f38d6cb924bb.png)

これらのグラフから、期待するピンの(x,y)座標とのズレを算出する式を導出しました。

```math
以下のように定義する. \\
実際のピンのx座標：x \\
実際のピンのy座標：y \\
期待するピンのx座標とのズレ：x' \\
期待するピンのy座標とのズレ：y' \\
```

```math
x' = 1.861214202 x \\
y' = 2.1844121085492 y + 24.813884593318
```

これらを用いて、期待するピンの(x,y)座標を算出するコードを作成しました。

```dart:期待するピンの(x,y)座標を算出するコード
expectPinX = actualPinX + 1.861214202     * actualPinX
expectPinY = actualPinY + 2.1844121085492 * actualPinY + 24.813884593318
```

### 効果確認

任意の位置のピンでも、タップすると反応するようになりました。

# 最後に

#### 背景の背景

"iPhone 13 Pro Max"で動作確認してた時に、この問題を発見しました。
当初は、「なんでこの端末だけ！？」と驚きました。
思い返すと、"Pixel 4"でのみしか動作確認してこなかった事に気付き、
端末問題では？と思い、切り分けをしました。
結果、iOS固有問題だと分かり、ちょっとだけ安心しましたね。

#### 感想

この記事の内容をやり切る事に、ほぼ1日を費やしました。。
終わった時には、「あれ？Flutterで開発をしてたんじゃなかったっけ？」と、作成したExcelシートを見ながら思いました。
追加したコードも、30行程度でしたしね。(iOS/Androidで処理を分けるので、少し膨らんだ印象です)
まー、何はともあれ、ちゃんと終わって良かった。。
安心して2021年を終えられます。
では。