kazuakix の日記

Windows Phone とか好きです

Bing Map API で住所から位置情報を取得する

初音さんが公開しておられる AED 検索用に地元の AED 一覧データを作成してみました。

ちなみに公開されているデータは安心の PDF 。まぁ、一元的に公開されているだけでもありがたいものですね。
 
まずは Word で開いて体裁を直しながら Excel にコピー。

f:id:kazuakix:20140816231722j:plain
 
これを PowerMap で表示するとこんな感じ。住所から勝手に位置情報を検索して地図上にプロットしてくれます。思った以上にまんべんなく配備されていますね。

f:id:kazuakix:20140816231728j:plain

この位置情報をもとのシートに反映できれば一件落着なのですが、どうも PowerMap で検索した結果を利用することはできないようです。
  
仕方ないので Fiddler を使って PowerMap の通信を盗み見してみると...

f:id:kazuakix:20140816232719j:plain
1 件ごとに Bing Map API 呼び出してるんか...
  
という訳で Bing Map API のキーを取得して同じことをしてみます。

盗み見た内容によると、問い合わせの URL はこんな形式です。*1

http://dev.virtualearth.net/REST/v1/Locations?adminDistrict=都道府県&locality=市区町村&addressLine=住所&key=APIキー&c=ja-JP

住所の部分は URL エンコードしてやります。

public string CreateRequestUri(string pref, string city, string address)
{
    return string.Format("http://dev.virtualearth.net/REST/v1/Locations?adminDistrict={0}&locality={1}&addressLine={2}&key={3}&c=ja-JP",
        WebUtility.UrlEncode(pref),
        WebUtility.UrlEncode(city),
        WebUtility.UrlEncode(address),
        API_KEY);
}

 
この URL にアクセスして 返ってくる JSON を Visual Studio に食べさせるとこんなクラスができました。*2

public class Rootobject
{
    public string authenticationResultCode { get; set; }
    public string brandLogoUri { get; set; }
    public string copyright { get; set; }
    public Resourceset[] resourceSets { get; set; }
    public int statusCode { get; set; }
    public string statusDescription { get; set; }
    public string traceId { get; set; }
}

public class Resourceset
{
    public int estimatedTotal { get; set; }
    public Resource[] resources { get; set; }
}

public class Resource
{
    public string __type { get; set; }
    public float[] bbox { get; set; }
    public string name { get; set; }
    public Point point { get; set; }
    public Address address { get; set; }
    public string confidence { get; set; }
    public string entityType { get; set; }
    public Geocodepoint[] geocodePoints { get; set; }
    public string[] matchCodes { get; set; }
}

public class Point
{
    public string type { get; set; }
    public float[] coordinates { get; set; }
}

public class Address
{
    public string addressLine { get; set; }
    public string adminDistrict { get; set; }
    public string countryRegion { get; set; }
    public string formattedAddress { get; set; }
    public string locality { get; set; }
    public string postalCode { get; set; }
}

public class Geocodepoint
{
    public string type { get; set; }
    public float[] coordinates { get; set; }
    public string calculationMethod { get; set; }
    public string[] usageTypes { get; set; }
}


Json.NET を使って ここに結果を読み込みます。
通信の結果は StatusCode でチェックしますが、検索結果が 0 件の場合でも 200 が返ってくるようなので estimatedTotal で件数のチェックも行っています。

private async Task GetGeocode()
{
    foreach (var a in this._addresses)
    {
        var uri = CreateRequestUri(a.Pref, a.Address, a.City);
        using (var req = new HttpClient())
        {
            var res = await req.GetStringAsync(uri);
            var json = JsonConvert.DeserializeObject<Rootobject>(res);

            if (json.statusCode == 200 && 0 < json.resourceSets[0].estimatedTotal)
            {
                // 緯度・経度
                a.Latitude  = json.resourceSets[0].resources[0].point.coordinates[0];
                a.Longitude = json.resourceSets[0].resources[0].point.coordinates[1];
            }
        }
    }
}

 
本当は Office のアドインかなにかで作りたかったのですが、一回実行して結果を貰っちゃったのでどうしようかな...と。

*1:もちろんドキュメントにも書いてあります

*2:[編集]-[形式を選択して貼り付け]-[JSON をクラスとして貼り付ける]