Plus Codes（Open Location Code）を使う。C#で。

# はじめに
**Open Location Code**というジオコーディングの仕組みをご存知でしょうか。
ジオコーディングとは地名や住所に地理座標を与えることであり、典型的には経緯度がそれにあたります。
様々な地理サービスやGPSで位置情報を示す値として使われています。

この経緯度とは別のジオコーディングの仕組みが**Open Location Code（OLC）**です。
これは以下のような表記によって地球上の位置を表すことができます。

```
8Q9WCVW2+JFJ
```

このコードは以下のようにそれぞれの桁に意味があり、地球上を格子状に段階的に分割していき、それぞれの格子にコードを割り当てることで任意の地点を一意のコードで表現するようになっています。
詳しくは[こちら](https://en.wikipedia.org/wiki/Open_Location_Code)。
これによるとコードの各桁は以下のような意味を持ちます。

8Q : 2200Km四方の範囲指定
9W : 110Km四方の範囲指定
CV : 5.5Km四方の範囲指定
W2 : 275m四方の範囲指定
JF : 14m四方の範囲指定
J  : 3.5m四方の範囲指定

これはGoogleが作った仕組みで、このコードをGoogleで検索すればその位置が表示されます。
前述のコードは長岡市役所の位置でした。
3.5mってかなりピンポイントに指定できますね。

<img src="https://qiita-image-store.s3.amazonaws.com/0/245438/e436f6f5-70b1-edb5-5b72-cbb57cdd804b.png" title="" alt="image.png" width="60%" height="" >

また、このOpen Location Codeを使ったジオコーディングのWebサービスが「**[plus+codes](https://plus.codes/)**」です。Googleが作ったサービスです。
このサイトではOpen Location Codeに関する情報と、コードや地名から地理的な位置を検索し、指定された範囲を示してくれたりします。
例えば、plus+codesのページに移動して、下のほうにある検索ボックスに「Niigata,Nagaoka」（日本語も可）と入力してボタンを押すと、以下のように表示されます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/245438/0ae9f494-8837-b2bd-d436-5ca6d26080c7.png" title="" alt="image.png" width="60%" height="" >

これからの説明において、いくつかOpen Location Codeの記述例が出てきますが、plus+codesで試したりするとわかりやすいかなと思います。

位置を指す方法として経緯度や住所があるわけですが、それらと比べて何が便利なのか、というと以下の点が挙げられます。

- 経緯度だと二つの値が必要だったところが、一つの値で位置を指すことができる
- 住所がない場所も指すことができる
- **ローカル環境で他のサービス（API）を使わず経緯度と相互変換できる**つまりローカルで経緯度を算出できる
- モバイルのGoogleマップアプリは、位置情報として経緯度ではなくOLCを載せている

3つ目と4つ目が非常に大事です。
例えば、住所や地名、店名から経緯度を求める場合はGoogle Geocoding APIなどを使う必要があります。
ある程度は無料ですが、それを超えると従量課金になると思います。
そういったサービスを介することなく、コードがわかれば経緯度を求めることができます。
個人にとって**無料は正義**です。
そしてもう一つ、モバイルGoogleマップアプリでは位置情報がコードとして表示されるので、Googleマップで検索して、その検索結果を自作のアプリケーションで利用する、ということがやりやすくなっています。
<img src="https://qiita-image-store.s3.amazonaws.com/0/245438/d5663f4e-7a56-c022-ad51-11b0c99f5e17.png" title="" alt="dfaldkfja.png" width="60%" height="" >


ということで今回は、ゆくゆくはモバイルアプリケーションで使うことを目標にして、まずはC#でこの`Open Location Code（OLC）`を使う方法について説明したいと思います。

#コンソールアプリのプロジェクトを作成
今回はシンプルにVisual Studioでコンソールアプリの作って、試してみましょう。


#ライブラリの取得
`OLC`を扱う実装は多くの言語で用意されていて、[このGitHubリポジトリ](https://github.com/google/open-location-code)から取得できます。
CからJavaScript、Go、Ruby、Pythonなどなど、大体の言語で使えるようになっています。すごいですね。

<img src="https://qiita-image-store.s3.amazonaws.com/0/245438/d1e29589-18dc-452c-1162-a4178d44568e.png" title="" alt="image.png" width="60%" height="" >

そしてみんな大好きC#の実装もありますので、リンクをたどっていくと[こちらのリポジトリ](https://github.com/JonMcPherson/open-location-code)にたどり着きますので、感謝しつつ自分のPCにダウンロードします。

<img src="https://qiita-image-store.s3.amazonaws.com/0/245438/bc604862-6b24-8ee4-7a4f-e31c5128374c.png" title="" alt="dafkjlfadsj.png" width="60%" height="" >

#ライブラリをプロジェクトに追加
ダウンロード（またはgit clone）したら、それを下の画像のようにフォルダをたどっていくと、`OpenLocationCode.dll`というファイルがあるので、これを自分のプロジェクトに「参照の追加」をします。
<img src="https://qiita-image-store.s3.amazonaws.com/0/245438/3b364cb8-9752-80ea-42b0-2738af491e6c.png" title="" alt="image.png" width="60%" height="" >

参照の追加は以下のようにします。
毎度おなじみ、ソリューションエクスプローラー上で`依存関係`を右クリックして`参照の追加`をクリックして、ダイアログが開いたら「参照」ボタンを押して`OpenLocationCode.dll`を指定すればOKです。
<img src="https://qiita-image-store.s3.amazonaws.com/0/245438/76762893-b196-5d51-1ebe-53d71c9e8641.png" title="" alt="image.png" width="60%" height="" >


#コードから経緯度を求める
コードから経緯度を求めるには、以下のようにします。

```csharp
using Google.OpenLocationCode;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Runtime.Serialization.Json;


namespace OpenLocationCodeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            //グローバルコードはそのままいける
            var locationCode = "8Q9WCVW2+JFJ";
            var olc = new OpenLocationCode(locationCode);//グローバルコードを入れれば、その経緯度をローカルで計算できる。
            var decoded = olc.Decode();

            Console.WriteLine($"GlobalCode:::lat:{decoded.CenterLatitude}, lon:{decoded.CenterLongitude}");

            Console.ReadKey();//コンソールが閉じないように。
        }
    }
}
```

`グローバルコード`という言葉がでてきますが、とりあえずそれはスルーしてください。
1行目で`Google.OpenLocationCode`という名前空間をusingで追加しています。
使い方は簡単で、

1. OpenLocationCodeのインスタンスを作成
2. インスタンス作成時にコードを文字列で渡す。
3. Decodeメソッドでコードから経緯度を計算。
4. Decodeメソッドの返り値に含まれる経緯度情報を出力

これだけです。
ブレークポイントを張ってDecodeメソッドの返り値の中身をみてみます。
これが指定したコードが表す格子の経緯度情報です。

```
		CenterLatitude	37.4465875	double
		CenterLongitude	138.851140625	double
		EastLongitude	138.85115625	double
		LatitudeHeight	2.5E-05	double
		LongitudeWidth	3.125E-05	double
		NorthLatitude	37.4466	double
		SouthLatitude	37.446575	double
		WestLongitude	138.851125	double
```

その格子の北端、南端の緯度と、西端、東端の経度、そして格子の中心の経緯度、格子の幅、高さをプロパティに持つシンプルなオブジェクトです。

今回は格子の中心の経緯度を表示しています。


#OLCの表現のバリエーション
OLCの書き方にはいくつかのバリエーションがあります。
大きく分けてグローバルコードとローカルコードです。

##グローバルコード
今回の例では一番詳細なコードの書き方（`8Q9WCVW2+JFJ`）を使いました。
これは`グローバルコード`といいます。
グローバルコードは後で説明する`ローカルコード`とは異なり、単独で位置を示すことができます。

もっとざっくりした範囲を指定したいのであれば、下の桁から削っていくことで格子の大きさを変えることができます。
例えば、大体14m四方ぐらいの精度でいいや、ということであれば、`8Q9WCVW2+JF`のように下1桁を消します。
さらには、大体275m四方ぐらいの精度でいいや、ということであれば、`8Q9WCVW2+`のように下3桁を消します。
それではさらに削れるかというと、今度は削るのではなく、「0」で埋めていきます。
例えば、以下のようにすると、大体110km四方の範囲を指定することになります。

```
8Q9W0000+
```

##ローカルコード
ローカルコードはグローバルコード比べて桁数が少なくなります。グローバルコードの先頭4桁がなくなり、「4桁+2桁」の形になります。
しかし、ローカルコードは単独では位置を示すことはできません。
ローカルコードは基準となる地名とセットで使うことで位置を示すことができます。

```
CVW2+JFJ Niigata
```

これはつまり、消えた4桁の代わりになる程度の情報を地名で補うことができなければならないということでしょう。
ですので、この「基準となる地名」は指定したい地域の近くである必要があります。遠すぎてはいけません。
ちなみに、以下のように「Niigata」ではなく「Tokyo」と指定すると、アクアライン近くの海の上を指すことになります。

```
CVW2+JFJ Tokyo
```
<img src="https://qiita-image-store.s3.amazonaws.com/0/245438/5b5d5bc9-9d10-8b3a-cf21-730979c728be.png" title="" alt="image.png" width="60%" height="" >



これもグローバルコードと同じく、「+」記号以下のコードを削ることで格子の広さを変えることができます。

```
CVW2+ Niigata
```




###C#でローカルコードを使うには？
グローバルコードについては上で書きました。
ではローカルコードはどうやってプログラムで使うのでしょうか。

まずはグローバルコードと同じようにやってみると、

```csharp
using Google.OpenLocationCode;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Runtime.Serialization.Json;


namespace OpenLocationCodeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var locationCode = "CVW2+JFJ Niigata";//ローカルコード+地名
            var olc = new OpenLocationCode(locationCode); //これはできない。例外発生
            var decoded = olc.Decode();

            Console.WriteLine($"LocalCodeWithCity:::lat:{decoded.CenterLatitude}, lon:{decoded.CenterLongitude}");

            Console.ReadKey();//コンソールが閉じないように。
        }
    }
}
```

ローカルコードは地名から得られる位置情報と組み合わせて、特定の位置を指すことができるものなので、地名から位置情報を求める必要がありますが、それにはGoogle Geocoding APIなどのサービスを使わなければなりません。

ローカルコードをC#で使う場合は、地名ではなく経緯度を使うことになります。ですので、Google Geocoding APIなどを使って基準となる地名の経緯度を取得して、それを使うか、**端末のGPS情報から経緯度を取得**することになります。

どちらの手段をとるかは置いておいて、まずはローカルコードと基準となる経緯度を使う方法を説明します。

```csharp
using Google.OpenLocationCode;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Runtime.Serialization.Json;


namespace OpenLocationCodeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            //ローカルコードは基準となる経緯度をもとに、計算する。
            locationCode = "CVW2+JFJ";
            olc = new OpenLocationCode(locationCode);

            //基準となる経緯度はローカルコードが指す場所の比較的近くである必要がある。
            //この例では「長岡市」を検索したときに得られる経緯度を使った。
            double lat = 14.9218848;//緯度:南北
            double lon = 138.7775175;//経度：東西
            var recoverdOlc = olc.Recover(lat, lon);//基準となる経緯度を指定してグルーバルコードのOpenLocationCodeオブジェクトを取得する。
            decoded = recoverdOlc.Decode();

            Console.WriteLine($"LocalCode:::lat:{decoded.CenterLatitude}, lon:{decoded.CenterLongitude}");

            Console.ReadKey();//コンソールが閉じないように。
        }
    }
}
```

`OpenLocationCode`のインスタンス化のときにローカルコードをコンストラクタに渡します。
次に`Recover`メソッドで基準となる経緯度を使用してグローバルコードを計算します。
`Recover`メソッドの返り値として得られるOpenLocationCodeオブジェクトの`Decode`メソッドを使えば、めでたく目的の経緯度を得ることができます。

位置情報を使ったアプリケーションであれば、基準となる経緯度として端末のGPS情報を使えば、外部のWebAPIを使わずにすみます。


#おわりに
Open Location Code、便利ですね。
しかもローカルだけで扱うこともできるというのは「オンラインであること」という制限を受けずにすむのが良いです。

さて、冒頭でも紹介しましたが、モバイルのGoogleマップアプリは、お店やピンを立てた位置の詳細情報の中にOpen Location Codeが記載されています。
しかし、残念ながらグローバルコードではなくてローカルコードで記述されています。
そのため、Google MapのAPIを使うか、端末のGPS情報を基準にしてRecoverメソッドを使って目的地の位置情報を求める必要があります。


APIを使わない場合は端末のGPS情報を使うことになりますが、もちろんAPIを使えるのであればより正確に便利に位置情報を扱うことができるのは言うまでもありません。

その場合はplus+codesが提供しているWebAPIを使うことができるので、`HttpClient`クラスを使ってリクエスト（JSON）を送ってレスポンス（JSON）を得たりしてください。

詳しくは[公式の説明](https://github.com/google/open-location-code/wiki/Plus-codes-API#api-request-format)を読んでください。
もしかしたら、記事人書くかもしれませんが。