Help us understand the problem. What is going on with this article?

Plus Codes(Open Location Code)を使う。C#で。

はじめに

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

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

8Q9WCVW2+JFJ

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

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

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

image.png

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

image.png

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

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

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

3つ目と4つ目が非常に大事です。
例えば、住所や地名、店名から経緯度を求める場合はGoogle Geocoding APIなどを使う必要があります。
ある程度は無料ですが、それを超えると従量課金になると思います。
そういったサービスを介することなく、コードがわかれば経緯度を求めることができます。
個人にとって無料は正義です。
そしてもう一つ、モバイルGoogleマップアプリでは位置情報がコードとして表示されるので、Googleマップで検索して、その検索結果を自作のアプリケーションで利用する、ということがやりやすくなっています。
dfaldkfja.png

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

コンソールアプリのプロジェクトを作成

今回はシンプルにVisual Studioでコンソールアプリの作って、試してみましょう。

ライブラリの取得

OLCを扱う実装は多くの言語で用意されていて、このGitHubリポジトリから取得できます。
CからJavaScript、Go、Ruby、Pythonなどなど、大体の言語で使えるようになっています。すごいですね。

image.png

そしてみんな大好きC#の実装もありますので、リンクをたどっていくとこちらのリポジトリにたどり着きますので、感謝しつつ自分のPCにダウンロードします。

dafkjlfadsj.png

ライブラリをプロジェクトに追加

ダウンロード(またはgit clone)したら、それを下の画像のようにフォルダをたどっていくと、OpenLocationCode.dllというファイルがあるので、これを自分のプロジェクトに「参照の追加」をします。
image.png

参照の追加は以下のようにします。
毎度おなじみ、ソリューションエクスプローラー上で依存関係を右クリックして参照の追加をクリックして、ダイアログが開いたら「参照」ボタンを押してOpenLocationCode.dllを指定すればOKです。
image.png

コードから経緯度を求める

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

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

image.png

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

CVW2+ Niigata

C#でローカルコードを使うには?

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

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

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情報から経緯度を取得することになります。

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

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)を得たりしてください。

詳しくは公式の説明を読んでください。
もしかしたら、記事人書くかもしれませんが。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away