この項では、
- 位置情報を取得する
- 位置情報を住所の文字列に変換する
方法について書きます。
1. 位置情報の取得
現在の位置情報を取得
Namespace Windows.Devices.Geolocation にあるGeolocator Class を使います。
// Appの初期化の辺りで一発Geolocatorを作成しておき、
this.geolocator = new Geolocator();
...
// 位置情報が必要な時にGetGeopositionAsyncを呼ぶ。
var pos = await this.geolocator.GetGeopositionAsync();
ここで要注意なのは、
- 位置情報を取得するアプリは、AppxManifest.xml内で位置情報の使用を宣言する必要があること
- 起動後初回の使用では「位置情報の取得」の確認メッセージが出ること
- GetGeopositionAsync では緯度経度のみが取得でき、住所データは取得出来ない事(今のところ)
1. については、VSのManifest Designerでチェックを入れるだけです。又、ストア上にはこのアプリが位置情報を使用する旨が表示されます。
2. これはAPIをCallしたところ(上の例だとGetGeopositionAsync)で問答無用に表示されます。
このため、なるべく、Userの操作の結果(ボタン押下のハンドラ等)…「○○をやろうとした結果」として呼ばれるところでAPIをCallすべきと思います。
Userの操作の無い所…初期化中等…でいきなりこれを出すのはあまり上品では無いように思います。そういうアプリたまに見ますが。
3. GetPositionAsync の返り値 として帰るGeoposition Classには以下二つのProperty…
があり、前者には住所文字列、後者には緯度経度が入る、事になっています。
が、前者CivicAddressは、Address Provider、緯度経度を住所に変換するプロバイダがインストールされていない場合常に空文字列で返ります。
そして、現在のところAddress Providerは何処からも提供されていないです。
画像の位置情報を取得
JPEG画像にEXIF データとして位置情報が埋め込まれている場合があります。
これは大変に簡単で、画像ファイルのStorageFileからImagePropertiesを取得します。もし緯度経度データが存在すれば、ImageProperties.LatitudeとLongitudeにデータが詰まっています。
※ 画像が「ファイル」である場合…Storage上のFileにはこれでいけるのですが…例えば、一度OpenFileAsyncしてStreamに全部ガーっと呼んだメモリ上のデータや、Httpか何かでがーっとDLしてまだファイルに落として無いデータ、に対して使用することは出来ないです。困った。
2. 位置情報を住所文字列に変換する
上で触れたようにWinRT組み込みのGeolocator Classだけでは不可能で、Bing.Maps APIを使う事になります。
緯度経度をAPI経由でBingに投げると、住所に変換して返してくれるという感じです。
Bing APIの使用には
- アカウント登録とAPI Keyの作成
- Bing Maps SDKのインストールと参照の追加
が必要になります。方法はMSのサイトでも見て頂ければいいのですが、無料の場合、
- キーは3つまで
- 50,000 Transaction/Day
となっています。間違えていると怖いのでご自分でご確認下さい。
※ Bing Maps API は Architecture毎のNative BinaryとしてBuildされています。このため、このAPIを「使う」Moduleは(.NETで書いたC#であっても)「Any CPU」としてBuildすることが出来なくなります。
Bing Maps API を使用した住所の取得
ここまで行くとソース見たほうが早いべ、という事でソースを貼って終わります。
注意点としては、LocationData は複数返ってくるので、中の属性を見て必要なものだけ拾う、という所です。
※ 以下の例は、アプリ「ごみ出しカレンダー」で使っているCodeです。
ごみ種別の入力画面でアプリバー上の「検索」ボタンを押すと、PCの現在地を使ってごみ収集ページを検索してあげる、という機能です。(自治体ごとに千差万別のごみ出しページをスクレイピングとかやってられないので、そこから先は手動です)
ごみ出しカレンダー
http://apps.microsoft.com/windows/app/c3cdaa23-5b99-4b28-803f-00b90c86a601
private async void GetAddressAppBarButton_Click(object sender, RoutedEventArgs e)
{
//「PCの位置情報から自治体のごみ出しページを検索してあげる」
var loader = new Windows.ApplicationModel.Resources.ResourceLoader();
var pos = await this.geolocator.GetGeopositionAsync();
Bing.Maps.Search.ReverseGeocodeRequestOptions requestOptions =
new Bing.Maps.Search.ReverseGeocodeRequestOptions(new Location(pos.Coordinate.Point.Position.Latitude, pos.Coordinate.Point.Position.Longitude));
var searchManager = this.bingMaps.SearchManager;
var response = await searchManager.ReverseGeocodeAsync(requestOptions);
string query = "";
Bing.Maps.Search.GeocodeAddress addr = null;
if (null != response && 0 < response.LocationData.Count)
{
for (int i = 0; i < response.LocationData.Count; i++)
{
/// 住所表示に使うLocationDataを探すCode
/// Bing.Mapsでは、Responseに複数のLocationDataが詰まって返ってくることがある。(普通は一個だが、たまに二つ)
/// 一つは地図上での住所表示用データ ... GeocodeLocationUsageType 'Display'
/// もう一つはナビ用の道路情報。 ... GeocodeLocationUsageType 'Route' または 'Both'.
/// 後者については住所の文字列が入っていないので、今回は必要が無いデータとなる。それを弾いて住所表示用だけをPickする。
/// (後者は大体Nameに「MajorRoad」とかが入ってる)
if (null != response.LocationData[i].GeocodeLocations && 0 < response.LocationData[i].GeocodeLocations.Count)
{
if (Bing.Maps.Search.GeocodeLocationUsageType.Display == response.LocationData[i].GeocodeLocations[0].UsageType)
{
/// 'Display'が複数ある場合は… 知らないし 最初の一つだけPickする
addr = response.LocationData[i].Address;
break;
}
}
}
if( null != addr )
{
/// StrWebQuery ... "ごみ+収集日+"
/// AdminDistrict ... "神奈川県" 大体県まで
/// AdminDistrict2 ... データ見たこと無いのだけど 郡とか?
/// Locality ... "川崎市高津区" 大体市+区まで
/// AddressLine ... "foo町bar丁目1-2-3" 最後のアドレス ごみ収集日検索では却って結果出なくなるので使わない
query = loader.GetString("StrWebQuery");
if( 0 < addr.AdminDistrict.Count() ) query += (addr.AdminDistrict + "+");
if( 0 < addr.AdminDistrict2.Count() ) query += (addr.AdminDistrict2 + "+");
if( 0 < addr.Locality.Count() ) query += (addr.Locality );
}
// URL形式にエンコ %1234...みたいなやつ
query = System.Net.WebUtility.UrlEncode(query);
// ブラウザ起動
await Windows.System.Launcher.LaunchUriAsync(new Uri( "http://www.google.com/search?q=" + query));
}
else
{
// 何もすることが無い
// Errormsgも出さなくていいかなと
}
}
}