はじめに
皆さんは、緯度・経度などの空間データをデータベースで扱ったことはありますか? たとえば、書店の場所をデータベースに格納し、ある地点から1km以内の書店を探すといった場合などです。SQL Server 2008からは空間データ(Spatial Data)型が利用できるようになり、そういったアプリ開発に使用できます。
SQL Serverと同じようには使えませんが、Bing Spatial Data Services で提供されているAPIを利用すれば、無償で空間データ用のクラウドデータベースが利用できます。このサービスを利用すれば、書店を探すアプリも作れるかもしれません。少し興味がわきましたか? 今回はこのBing Spatial Data Servicesを使ってみましょう!
Bing Spatial Data Services
Bing Spatial Data Servicesの大きな特徴として、RESTサービスであることがあげられます。すべての操作はHTTP(またはHTTPS)で行い、データはXMLやJSONなどを利用します。Bing Maps REST ServicesというREST API群もあり、リクエストやレスポンスのフォーマットはこれらと共通になっています。Bing Maps REST Servicesは第9回 から第11回 で紹介しています。
Bing Spatial Data Servicesは、大きくわけて2種類のサービスがあります。多数の地理情報のジオコーディング・逆ジオコーディングサービスと、冒頭で述べた地理情報を含むデータのストアと検索サービスです。これらのサービスは、次の3種類のAPIから利用します。
Geocode Dataflow API
住所や経緯度の地理情報の含む多数のデータを、一括でジオコーディング・逆ジオコーディングするAPIです。実は、Bing Spatial Data Servicesについては、一度第12回 で紹介しています。このときは、このGeocode Dataflow APIしかありませんでしたが、2011年2月のアップデートにより次の2種類のAPIが追加されています。
Data Source Management API
このData Source Management APIと次のQuery APIが、今回のテーマとなる、空間データのデータベースを扱うAPIです。Spatial Data Servicesではデータベースという言葉は使わず、ユーザー(開発者)が定義およびアップロードしたデータをData Source と呼びます。このAPIでは、Data Sourceの作成(データのアップロード)や、削除、データ定義の参照などを行います。
Query API
3個目は、Data Sourceにあるデータを問い合わせるAPIです。Data Sourceの制限として、一部のデータを更新したり削除することはできません。つまりData Sourceからデータの検索のみ行えます。このAPIによって、ある地点から1kmに含まれるデータなどの検索ができます。位置情報を使った検索だけでなく、別の項目(たとえば書店名)などから検索も可能です。ただし文字列の検索の場合、完全一致か不一致しか扱えません。
特長と制限
Bing Spatial Data Servicesのデータストアと検索サービスの特長は、既に紹介したように、無償であり、JavaScriptからも利用できるRESTサービスであり、また空間データを扱えます。そして、ユーザーが定義したデータをBingサービスのサーバー上にアップロードして利用できます。アプリ側で空間データ用のストレージを用意する必要はありません。
制限としては、圧縮していない状態でひとつのData Sourceは300MBまで、テキスト情報はUTF-8エンコーディングのみ。ひとつのアカウントにつき作成できるData Sourceは25個までなどの制限があります。また、Data Sourceの一部のデータの削除や更新は現状ではできません。Data Sourceの更新は、Data Sourceの内容を入れ替えるということになります。
利用手順
Data Sourceの利用は、次のような手順になります。
地理情報を含んだデータの準備
データのアップロード(Data Sourceの作成)
データの検索
またサービスの利用には、これまでの連載と同様、Bing Maps Key が必要です。あらかじめBing Maps Account Center でアカウントを登録し、Bing Maps Keyを取得してください。
Bing Maps Keyについて、これまでとは異なる点があります。開発用に試す分には問題ありませんが、アプリを公開する場合は、2個のBing Maps Keyが必要ですので注意してください。Data Sourceの作成・削除を行うKey(マスターキー )と、Data Sourceの検索用のKey(クエリーキー )をわけて使う仕組みになっています。Bing Spatial Data ServicesはRESTサービスのため、アプリのユーザーは、アプリの通信内容からBing Maps Keyを知ることができます。マスターキーを公開してはいけません。
データの準備
それではData Sourceの元となるデータを準備します。ここではData Sourceを構成するデータの定義と実データを、書式に従ったテキストファイルとして作成します。
サンプルデータ
書店を題材にしてみましょう。書店は次のような情報(プロパティ)を持っています。
実際のデータは次のような感じになります。
ほげほげ書店, 名古屋県○○区, 052-XXX-XXXX
ぴよぴよ書房, 名古屋県□□区, 052-YYY-YYYY
ふがふが堂, 名古屋県△△区, 052-ZZZ-ZZZZ
ひとつのデータには必ず、経度と緯度が含まれている必要があります。またID(一意な文字列)も必要です。それ以外の情報は上記のプロパティのように自由に定義できます。IDと緯度・経度を追加したデータは次のようになります。
001, 34.98261, 136.79583, ほげほげ書店, 名古屋県○○区, 052-XXX-XXXX
002, 34.97023, 136.69696, ぴよぴよ書房, 名古屋県□□区, 052-YYY-YYYY
003, 34.89705, 136.74502, ふがふが堂, 名古屋県△△区, 052-ZZZ-ZZZZ
プロパティの定義
これらのデータに、プロパティ名と型を定義します。プロパティ名は次の制約があります。
英数字とアンダースコア(_)を使用
最大50文字
先頭の文字は数字以外
2文字のアンダースコア(__)で始まる名前は禁止
大文字・小文字は区別しない
また、必須の緯度と経度のプロパティは、Latitude とLongitude になります。オプションとして位置情報に高度を含める場合は、Elevation を使用します。
プロパティに使用できるデータ型は、次の5種類です。OData (Open Data)というHTTPベースのデータサービスのための規格の型名を使用しています。
データ型 OData Type ひとつのデータに使用できる個数
文字列 Edm.String 50(最大文字数は256文字)
整数 Edm.Int64 20
論理 Edm.Boolean 40
実数 Edm.Double 20
日時 Edm.DateTime 5
今回のデータでは、次のように定義しました。
名前 データ型 説明
ID Edm.String ID
Latitude Edm.Double 緯度
Longitude Edm.Double 経度
Name Edm.String 店名
Address Edm.String 住所
Tel Edm.String 電話番号
ちなみに、今回使用していない論理型はtrue・falseのいずれか、日時型の場合は「2011-06-14T12:00」のように値を表現します。
ファイルの書式
データは、XML形式、または CSV(カンマ区切り) 、タブ区切り、パイプ(|)区切りのテキストファイルとして用意します。文字エンコーディングはUTF-8です。CSVは単純なカンマ区切りで、カンマを含む文字列の値などは扱えません。
今回は、XML形式は割愛し、区切り文字によるテキストファイルを作成します。例では、区切り文字にパイプを使用します。
まずファイルの1行目に、Bing Spatial Data ServicesのバージョンとData Sourceのエンティティの種類の名前(データのコレクションを表す名前)を記述します。現在のサービスのバージョンは1.0です。
Bing Spatial Data Services, 1.0, Bookstores
2行目は、プロパティ名と型の定義を記述します。「 プロパティ名(型名) 」のように記述し、区切り文字で区切ります。IDとなるプロパティは「プロパティ名(型名, primaryKey) 」と記述します。
ID(Edm.String,primaryKey)|Latitude(Edm.Double)|Longitude(Edm.Double)|Name(Edm.String)|Address(Edm.String)|Tel(Edm.String)
3行目以降には実際のデータを記述します。最終的には以下の内容のテキストファイルができます。
Bing Spatial Data Services, 1.0, Bookstores
ID(Edm.String,primaryKey)|Latitude(Edm.Double)|Longitude(Edm.Double)|Name(Edm.String)|Address(Edm.String)|Tel(Edm.String)
001|34.98261|136.79583|ほげほげ書店|名古屋県○○区|052-XXX-XXXX
002|34.97023|136.69696|ぴよぴよ書房|名古屋県□□区|052-YYY-YYYY
003|34.89705|136.74502|ふがふが堂|名古屋県△△区|052-ZZZ-ZZZZ
以上でアップロードするデータの準備ができました。
データのアップロード
次は用意したテキストファイルをData Source Management APIを利用してサーバーへアップロードします。アップロードしてすぐにData Sourceが作成されるわけではなく、実際にはData Source作成のジョブを登録する作業になります。
リクエスト
Data Sourceの作成(ジョブの登録)は、次のURLへHTTP POSTメソッドでテキストファイルをアップロードします。
http://spatial.virtualearth.net/REST/v1/Dataflows/LoadDataSource?
dataSourceName=dataSourceName &
loadOperation=complete &
input=pipe &
output=json &
key=masterKey &
queryKey=queryKey
URLのパラメーターの内容は次の通りです。
名前 説明
dataSourceName Data Source名
50文字以下
次の記号は使えません
*?"<>:|\\
loadOperation complete を指定
Data Source内のすべてのデータを新しいデータに置き換えます
input 入力形式
次のいずれか
xml, csv, tab, pipe
output レスポンスの形式
xmlまたは json(規定の値)
key マスターキーとなるBing Maps Key
queryKey クエリーキーとなるBing Maps Key
オプションですが指定推奨です
HTTPリクエストヘッダーのContent-typeの値は、入力形式に応じて、次のいずれかである必要があります。
application/xml
text/xml
text/plain
application/octet-stream
POSTデータに含めるのではなく、Windows AzureのデータストレージのBLOBリソースの指定する方法もあります。その場合はdataLocation パラメーターを追加し、dataLocation=http://***.blob.core.windows.net/myEntityData のように指定します。詳細については割愛します。
レスポンス
アップロードにより、Data Source作成のジョブが登録されました。レスポンスにはジョブの状態が含まれています。ジョブが正しく登録された場合の、JSON形式のレスポンスは次のようになります。
{
"authenticationResultCode" : "ValidCredentials" ,
"brandLogoUri" : "http://spatial.virtualearth.net/Branding/logo_powered_by.png" ,
"copyright" : "Copyright c 2011 Microsoft and its suppliers. (省略)" ,
"resourceSets" : [
{
"estimatedTotal" : 1 ,
"resources" : [
{
"__type" : "DataflowJob:http://schemas.microsoft.com/search/local/ws/rest/v1" ,
"createdDate" : "Wed, 08 Jun 2011 17:29:46 GMT" ,
"failedEntityCount" : 0 ,
"id" : "XXX" ,
"links" : [
{
"role" : "self" ,
"url" : "https://spatial.virtualearth.net/REST/v1/dataflows/LoadDataSource/XXX"
}
],
"processedEntityCount" : 0 ,
"status" : "Pending" ,
"totalEntityCount" : 0
}
]
}
],
"statusCode" : 201 ,
"statusDescription" : "Created" ,
"traceId" : "…"
}
レスポンス中の、status の値がPending になっていることがわかります。この値が、Pending の時はジョブが処理中を表しています。処理が終了するとCompleted 、エラーにより中断した場合はAborted です。
また、links コレクションに以下の書式のURLがあります(role の値がself であるurl の値) 。
https://spatial.virtualearth.net/REST/v1/dataflows/LoadDataSource/JobID ?
key=masterKey
ジョブの状態を確認するには、このURLにkey パラメーターを付けてアクセスすると最新のジョブ状態を得ることができます(同じ形式のレスポンスを得ます) 。
既に述べたようにリクエスト方法やレスポンスの内容は、Bing Maps Rest ServicesやBing Spatial Data ServicesのGeocode Dataflow APIと共通しています。少し詳しい内容は、これまでの連載(第9回 や第12回 )を参照してください。
サンプルコード
データをアップロードするC#のサンプルコードを示します。Visual C# 2010 Express やVisual Studio 2010からC#のコンソールアプリケーション プロジェクトを作成し、下記のコードをMainメソッド内に記述すると動作します。コード中のファイルパスやBing Maps Keyは環境にあわせて変更してください。
var file = @ "C:\Users\Owner\Documents\bookstores.txt" ;
var name = "GihyoSample" ;
var masterKey = "MasterKey" ;
var queryKey = "QueryKey" ;
var url = string . Format ( "http://spatial.virtualearth.net/REST/v1/Dataflows/LoadDataSource?dataSourceName={0}&loadOperation=complete&input=pipe&output=json&key={1}&queryKey={2}" ,
name , masterKey , queryKey );
using ( var reader = new System . IO . StreamReader ( file , System . Text . Encoding . UTF8 ))
{
using ( var client = new System . Net . WebClient ())
{
client . Encoding = System . Text . Encoding . UTF8 ;
client . Headers [ System . Net . HttpRequestHeader . ContentType ] = "text/plain" ;
var response = client . UploadString ( url , reader . ReadToEnd ());
Console . WriteLine ( response );
}
}
実行すると、JSON形式のレスポンスが表示されます。もし処理の結果エラーのレスポンスが返ってきたり、ジョブの処理が中断している場合は、エラーメッセージ内容、データの形式が正しいかなどを確認してください。
データの検索
データをアップロードし、ジョブの処理が完了すると、Data Sourceが作成完了し、データの検索ができるようになります。最後は、作成したData Sourceからデータを検索してみましょう。検索はQuery APIを利用します。HTTP・HTTPSの両方を使用でき、制限として一度に取得できる結果は最大250件です。
リクエストとレスポンス
データの検索は、次のURLへHTTP GETメソッドでアクセスします。ここで示しているURLは地理情報から検索する場合です。Query APIでは、地理情報を検索条件に使用した場合、ある経緯度から半径○km以内の結果を取得する方法と、南北の緯度・東西の経度を指定し、その範囲内の結果を取得する方法があります。
http://spatial.virtualearth.net/REST/v1/data/accessId /dataSourceName /entityTypeName ?
spatialFilter=nearby(latitude,longitude,distance) &
queryoption1 &
queryoption2 &
:
queryoptionN &
jsonp=jsonCallBackFunction &
jsonso=jsonState &
key=queryKey
URLのパラメーターは次の通りです。
名前
説明
accessId
Data SourceのID(後述)
dataSourceName
Data Source名
entityTypeName
Data Sourceのエンティティの種類の名前
spatialFilter
地理情報による検索範囲の指定(後述)
queryoption1, 2, …N
クエリーのオプション 取得する形式や項目、並び替えなどを指定します(後述)
jsonp
JSONP形式で取得する場合、JavaScriptのコールバック関数名を指定 例:jsonp=MyCallbackFunction
jsonso
jsonp使用時に任意のデータを指定。コールバック関数の第2引数の値になる 例:jsons
queryKey
クエリーキーに指定したBing Maps Key
URLのパスの、dataSourceName は、データアップロード時にパラメーターに指定した値です。entityTypeName は、アップロードしたデータの中に指定した値(1行目に記述したコレクションの名前)です。例ではそれぞれ、GihyoSampleとBookstoresと指定していました。
レスポンスで得られるデータは、JSON形式の場合次のようになります。results コレクションに条件に一致したデータが含まれています。
{
"d" : {
"__copyright" : "© 2011 Microsoft and its suppliers. " ,
"results" : [
{
"Address" : "名古屋県△△区" ,
"ID" : "003" ,
"Latitude" : 34.89705 ,
"Longitude" : 136.74502000000001 ,
"Name" : "ふがふが堂" ,
"Tel" : "052-ZZZ-ZZZZ" ,
"__metadata" : {
"uri" : "https://spatial.virtualearth.net/REST/v1/data/XXX/GihyoSample/Bookstores('003')"
}
}
]
}
}
Atom形式の場合は次のような内容になります。
<feed xmlns:d = "http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns = "http://www.w3.org/2005/Atom" >
<title type = "text" >
</title>
<id> ... </id>
<rights type = "text" >
© 2011 Microsoft and its suppliers.
</rights>
<updated>
2011-06-12T19:45:13Z
</updated>
<entry>
<id>
https://spatial.virtualearth.net/REST/v1/data/xxx/GihyoSample/Bookstores('003')
</id>
<title type = "text" >
</title>
<updated>
2011-06-12T19:45:13Z
</updated>
<content type = "application/xml" >
<m:properties>
<d:id>
003
</d:id>
<d:latitude m:type = "Edm.Double" >
34.897050
</d:latitude>
<d:longitude m:type = "Edm.Double" >
136.745020
</d:longitude>
<d:name>
ふがふが堂
</d:name>
<d:address>
名古屋県△△区
</d:address>
<d:tel>
052-ZZZ-ZZZZ
</d:tel>
</m:properties>
</content>
</entry>
</feed>
今回は割愛しますが、地理情報を条件とする以外にも、プロパティの値を条件とすることも可能です。絞り込みも可能です。
続いて、検索時のパラメーターについてもう少し詳しくみてみましょう。
Access ID
accessId (Access ID )は、Data Sourceごとに割り当てられるIDです。この値は、検索の前にあらかじめこのIDの値を取得しておく必要があります。Access IDを知るには、いくつか方法があります。
ひとつは、データのアップロードのときに述べたジョブの状態を確認する方法です。ジョブの状態がCompletedのとき、レスポンスのlinksコレクションの値を確認すると、次のようにrole の値がdataSource のものが含まれています。url の値を確認すると、accessId /dataSourceName /entityTypeName 形式のURLになっています。この方法は、ジョブが完了してから一定の日数のみ可能です。
もうひとつは、Bing Maps Keyに属しているData Sourceの一覧を取得する方法です。次のURLにGETメソッドでアクセスします。
http://spatial.virtualearth.net/REST/v1/data?
$format=json &
key=BingMapsKey
URLのパラメーターは次の通りです。
名前
説明
$format
取得する形式 atom(規定の値)または json
key
マスターキーまたはクエリーキーに指定したBing Maps Key
Atom(XML)形式の場合の結果は次のようになります。
<? xml version = '1.0' encoding = 'utf-8' ?>
<app:service xmlns:app = "http://www.w3.org/2007/app" xmlns:atom = "http://www.w3.org/2005/Atom" xmlns:bsi = "http://schemas.microsoft.com/bing/spatial/2010/11/odata" >
<bsi:copyright>
© 2011 Microsoft and its suppliers.
</bsi:copyright>
<app:workspace bsi:updated = "2011-06-08T17:29:46Z" >
<atom:title>
GihyoSample
</atom:title>
<app:collection app:href = "https://spatial.virtualearth.net/REST/v1/data/XXX/GihyoSample/Bookstores" >
<atom:title>
Bookstores
</atom:title>
</app:collection>
</app:workspace>
</app:service>
<app:workspace>要素内に、Data Source名、Entity Type名があることがわかります。<app:collection>要素のapp:href 属性が、accessId /dataSourceName /entityTypeName 形式のURLとなっており、ここからAccess IDがわかります。ただし、この方法の場合、JSON形式で取得した結果には、Access IDが含まれていません。
地理情報による検索条件の指定
次は、spatialFilter パラメーターについてです。ある地点から○km以内に含まれるデータを取得するには、次のように緯度・経度・距離(km)を指定します。
spatialFilter=nearby(34.9,136.7,5.0)
4点の東西南北の経緯度で指定した領域に含まれるデータを取得するには、次のように南の緯度、西の経度、北の緯度、東の経度の順で値を指定します。
spatialFilter=bbox(34.9,136.7,35.0,136.8)
クエリーオプションの指定
queryoptionNパラメーターは、ODataのクエリーオプションで定義されている内容です(ただしBing Spatial Data Serbvicesで使用できる内容には制限があります) 。いくつか例を示します。
取得するデータ形式
$format パラメーターで取得するデータ形式を指定できます。XML(Atom) ・JSON形式のいずれかです。
規定の値はatom です。
プロパティの選択
規定では結果に、すべてのプロパティが含まれています。取得するプロパティを選択するには$select パラメーターを使用して、プロパティ名をカンマ区切りで指定します。
取得件数とオフセットの指定
$skip と$top パラメーターを使用して、取得データの件数と位置を指定します。
$skip=0&$top=10 … ( データの1〜10件を取得)
$skip=10&$top=10 … ( データの11〜20件目を取得)
その他のクエリーオプションは、次の機会に紹介します。
サンプルコード
最後に、データ検索のサンプルコードを示します。データ検索の場合、HTTP GETメソッドですので、単純にWebブラウザーでリクエストURLにアクセスするだけでも結果を得られます(図1 )
図1 データの検索
データのアップロードサンプルと同じくC#のコンソールアプリケーションです。
static void Main ( string [] args )
{
var dataSrouceName = "GihyoSample" ;
var entityTypeName = "BookStores" ;
var accessId = "AccessID" ;
var queryKey = "QueryKey" ;
var spatialFilter = "nearby(34.9,136.7,5.0)" ;
var url = string . Format ( "http://spatial.virtualearth.net/REST/v1/data/{0}/{1}/{2}?$format=atom&key={3}&spatialFilter={4}" , accessId , dataSrouceName , entityTypeName , queryKey , spatialFilter );
XNamespace a = "http://www.w3.org/2005/Atom" ;
XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" ;
XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices" ;
var doc = XDocument . Load ( url );
foreach ( var entry in doc . Element ( a + "feed" ). Elements ( a + "entry" ))
{
var props = entry . Element ( a + "content" ). Element ( m + "properties" );
Console . WriteLine ( string . Format ( "{0}, {1}" , props . Element ( d + "Name" ). Value , props . Element ( d + "Address" ). Value ));
}
}
データはC#で扱いやすいAtom形式を指定しています。コードでは結果の書店名と住所を表示しています。
今回はここまでです。いかがでしたか。次回はData Source Management APIとQuery APIについてもう少し紹介して、簡単なWebアプリを作成する予定です。