はじめに
今回は、
作成するアプリは、


アプリはJavaScriptを使用して作成します。jQueryも利用しています。
また、

データの準備
Data Sourceとなるデータを準備します。今回は、
データの内容は、
- 燃料給油所の位置
(経緯度) - 燃料給油所の所在地
- 店舗分類を表すコード
店舗分類コードは、
実際にアプリを作成するとなると、
Data Sourceの定義
作成するData Sourceのプロパティは次のように定義します。
名前 | データ型 | 説明 |
---|---|---|
ID | Edm. | ID |
Latitude | Edm. | 緯度 |
Longitude | Edm. | 経度 |
Address | Edm. | 燃料給油所の所在地 |
Code | Edm. | 店舗分類コード |
店舗分類コードを含めて、
アップロード用のファイルは、
Bing Spatial Data Services, 1.0, FuelStores
ID(Edm.String,primaryKey)|Latitude(Edm.Double)|Longitude(Edm.Double)|Address(Edm.String)|Code(Edm.Int64)
データの変換
さて、
<jps:GM_Point id="n00597">
<jps:GM_Point.position>
<jps:DirectPosition>
<DirectPosition.coordinate>35.23375805 136.82025784</DirectPosition.coordinate>
<DirectPosition.dimension>2</DirectPosition.dimension>
</jps:DirectPosition>
</jps:GM_Point.position>
</jps:GM_Point>
<ksj:FG01 id="FG01_00597">
<ksj:POS idref="n00597"/>
<ksj:FSC codeSpace="FuelStoreCd.xml">1</ksj:FSC>
<ksj:ADS>愛知県稲沢市日下部北町3丁目XX-X</ksj:ADS>
</ksj:FG01>
位置情報と、
XMLファイルからData Source用のテキストファイルへの変換はどのような方法でもよいですが、
// 必要な using の宣言
//using System;
//using System.Collections.Generic;
//using System.IO;
//using System.Text.RegularExpressions;
//using System.Xml.Linq;
// 燃料給油所 情報のクラス
private class FuelStore
{
public string Id { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public string Address { get; set; }
public int Code { get; set; }
}
static private Dictionary<string, FuelStore> FuelStores = new Dictionary<string, FuelStore>();
// ひとつの XML ファイルから燃料給油所の情報を読み取る
static void LoadData(string prefix, string file)
{
var doc = XDocument.Load(file);
// 位置情報の読み取り
XNamespace jps = "http://www.gsi.go.jp/GIS/jpgis/standardSchemas";
foreach (var el in doc.Descendants(jps + "GM_Point"))
{
var store = new FuelStore();
var coordinate = el.Element(jps + "GM_Point.position").Element(jps + "DirectPosition").Element("DirectPosition.coordinate").Value;
var latlng = coordinate.Split(' ');
store.Id = prefix + el.Attribute("id").Value;
store.Latitude = Convert.ToDouble(latlng[0]);
store.Longitude = Convert.ToDouble(latlng[1]);
FuelStores.Add(store.Id, store);
}
// 所在地、店舗分類コードの読み取り
XNamespace ksj = "http://nlftp.mlit.go.jp/ksj/schemas/ksj-app";
foreach (var el in doc.Descendants(ksj + "FG01"))
{
var id = prefix + el.Element(ksj + "POS").Attribute("idref").Value;
var code = Convert.ToInt32(el.Element(ksj + "FSC").Value);
var address = el.Element(ksj + "ADS").Value;
if (FuelStores.ContainsKey(id))
{
FuelStores[id].Code = code;
FuelStores[id].Address = address;
}
}
}
static void Main(string[] args)
{
// ダウンロードした XML ファイルがあるフォルダーのパスを指定
var path = @"C:\Users\Owner\Documents\data";
// P07-10_ で始まる .xml ファイルから情報を読み取る
foreach (var file in System.IO.Directory.GetFiles(path, "P07-10_*.xml", SearchOption.AllDirectories))
{
var match = Regex.Match(file, "P07-10_(?<num>\\d+)\\.xml");
LoadData(match.Groups["num"] + "-", file);
}
// Data Srouce 用テキストファイルを作成
using (var fs = new FileStream("FuelStores.txt", FileMode.Create, FileAccess.Write))
{
using (var sw = new StreamWriter(fs, System.Text.Encoding.UTF8))
{
sw.WriteLine("Bing Spatial Data Services, 1.0, FuelStores");
sw.WriteLine("ID(Edm.String,primaryKey)|Latitude(Edm.Double)|Longitude(Edm.Double)|Address(Edm.String)|Code(Edm.Int64)");
foreach (var store in FuelStores.Values)
{
sw.WriteLine(string.Format("{0}|{1}|{2}|{3}|{4}", store.Id, store.Latitude, store.Longitude, store.Address, store.Code));
}
}
}
}
コード中のフォルダーのパスを変更して実行すると、
最終的に次のような形式のテキストファイルが用意できれば準備完了です。
Bing Spatial Data Services, 1.0, FuelStores
ID(Edm.String,primaryKey)|Latitude(Edm.Double)|Longitude(Edm.Double)|Address(Edm.String)|Code(Edm.Int64)
01-n00001|43.78809074|142.39757271|北海道旭川市永山2条X丁目|1
01-n00002|43.76126172|142.37049301|北海道旭川市宮下通15丁目YYYY-Y|1
01-n00003|43.76244953|142.41265587|北海道旭川市豊岡6条7丁目Z-Z|1
ちなみに、
データのアップロード
作成したテキストファイルをアップロードして、
Bing Maps AJAX Control
今回は、

Bing Maps AJAX Controlについては第14~17回でも紹介していますので、

地図の表示
最初に地図の表示部分を作ります。HTMLファイルに次のように記述します。Bing Maps AJAX ControlとjQueryのライブラリーを参照しています。Bing Maps AJAX Controlにも、
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>Fuel Stores</title>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.1.min.js" type="text/javascript"></script>
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&mkt=ja-jp"></script>
<script type="text/javascript">
<!--
$(function() {
var map = null;
var key = "BingMapsKey";
map = new Microsoft.Maps.Map(document.getElementById("map"), {credentials: key});
});
//-->
</script>
</head>
<body>
<div id="map" style="position: relative; width: 500px; height: 500px"></div>
</body>
</html>
上記コードでは、
データの検索
次は、
map = new Microsoft.Maps.Map(document.getElementById("map"), {credentials: key});
Microsoft.Maps.Events.addThrottledHandler(map, 'viewchangeend', loadData, 500);
Mapオブジェクトのviewchangeendイベントが発生すると、
続いてloadData関数を作成しましょう。ここでは、
検索する条件は、
- spatialFilter=bbox(35.
90,139. 66,36. 05,139. 84)
Mapオブジェクトの値を用いて、
var b = map.getBounds();
var s = b.getSouth();
var w = b.getWest();
var n = b.getNorth();
var e = b.getEast();
var bbox = "bbox(" + s + "," + w + "," + n + "," + e + ")";
HTTP GETメソッドでリクエストを行うコードは次のようになります。
function loadData() {
// spatialFilter パラメーター値の作成
var b = map.getBounds(); // LocationRect オブジェクトの取得
var s = b.getSouth();
var w = b.getWest();
var n = b.getNorth();
var e = b.getEast();
var bbox = "bbox(" + s + "," + w + "," + n + "," + e + ")";
// リクエストの送信
$.ajax({
type: "GET",
url: "http://spatial.virtualearth.net/REST/v1/data/accessID/dataSourceName/FuelStores",
dataType: "jsonp",
data: { // リクエストパラメーター
spatialFilter: bbox,
key: key,
$top: 250,
$format: "json"
},
jsonp: "jsonp",
success: function (data, dataType) {
addPins(data);
}
});
}
リクエストURLのaccessIDは、
dataオブジェクトで、
データの取得に成功した場合、
プッシュピンの表示
Query APIで取得した情報を使って、
- 燃料給油所の位置にプッシュピンを表示
- 地図の表示範囲外に出たプッシュピンは地図上から削除
- プッシュピンをクリックすると所在地をポップアップ表示
- 地図のズームレベルを変更するとポップアップを非表示
プッシュピンとポップアップを分けて管理するため、
map = new Microsoft.Maps.Map(document.getElementById("map"), {credentials: key});
Microsoft.Maps.Events.addThrottledHandler(map, 'viewchangeend', loadData, 500);
Microsoft.Maps.Events.addThrottledHandler(map, "viewchange", hideInfobox, 500);
var pins = new Microsoft.Maps.EntityCollection();
var infoboxes = new Microsoft.Maps.EntityCollection();
map.entities.push(pins);
map.entities.push(infoboxes);
上記コードでは、
プッシュピンの削除と追加
addPins関数を作成します。ここでは、
jQuery16105611959514236509_1308656047043(
{
"d": {
"__copyright": "© 2011 Microsoft and its suppliers. (省略)",
"results": [
{
"Address": "兵庫県神戸市北区道場町日下部X",
"Code": 3,
"ID": "28-n01424",
"Latitude": 34.85762838,
"Longitude": 135.22473655,
"__metadata": {
"uri": "https://spatial.virtualearth.net/REST/v1/data/accessId/dataSourceName/FuelStores('28-n01424')"
}
},
{
(省略)
}
]
}
})
※ 実際のデータは、
データを見ると、
for (var i = 0; i < data.d.results.length; ++i) {
var store = data.d.results[i];
var loc = new Microsoft.Maps.Location(store.Latitude, store.Longitude);
var pin = new Microsoft.Maps.Pushpin(loc);
pins.push(pin); // EntityCollection に追加
}
実際には、
削除処理までを記述した、
function addPins(data) {
if (!data || !data.d || !data.d.results) {
// 結果がない場合は処理を抜ける
return;
}
// 画面外に移動した Pushpin の削除と、画面内にある Pushpin をチェック
var addedPins = {};
var bbox = map.getBounds(); // LocationRect オブジェクトの取得
for (var i = pins.getLength() - 1; i >= 0; --i) {
var pin = pins.get(i);
if (!bbox.contains(pin.getLocation())) {
// 表示範囲外の Pushpin はイベントの関連付けを削除してから EntityCollection からも削除
Microsoft.Maps.Events.removeHandler(pin.clickHandler);
pins.removeAt(i);
} else {
addedPins[pin.id] = true;
}
}
// (ここに Pushpin を追加する処理を追記)
}
続いてPushpinの追加処理です。ここでは、
// 取得したデータ分の Pushpin を追加
for (var i = 0; i < data.d.results.length; ++i) {
var store = data.d.results[i];
if (addedPins[store.ID]) {
// 既に追加している場合は次のデータへ
continue;
}
var loc = new Microsoft.Maps.Location(store.Latitude, store.Longitude);
var pin = new Microsoft.Maps.Pushpin(loc, {text: (store.Code == 2) ? "LP" : (store.Code == 3) ? "etc" : ""});
// pin オブジェクトに独自のプロパティを設定し燃料料給油所の情報を保持
pin.id = store.ID;
pin.address = store.Address;
pin.code = store.Code;
// Pushpin のクリックイベントの関連付け
pin.clickHandler = Microsoft.Maps.Events.addHandler(pin, "click", showInfobox);
pins.push(pin); // EntityCollection に追加
}
おまけで、
ポップアップの表示
最後にポップアップ処理を記述します。イベントと関連付けて、
ポップアップは、
function showInfobox (e) {
infoboxes.clear(); // EntityCollection 内の Infobox を削除
// クリックされた Pushpin の情報から Infobox を生成
var pin = e.target;
var infobox = new Microsoft.Maps.Infobox(pin.getLocation(), {
title: (pin.code == 1) ? "ガソリンスタンド" : (pin.code == 2) ? "LPガススタンド" : "SS以外の燃料販売店",
description: pin.address,
offset: new Microsoft.Maps.Point(0, 20),
height: 100,
visible: true});
// Infobox を表示 (Infobox 用の EntityCollection に追加)
infoboxes.push(infobox);
}
hideInfobox関数は、
var zoom = 0;
function hideInfobox () {
var targetZoom = map.getTargetZoom();
if (zoom != targetZoom) {
infoboxes.clear();
zoom = targetZoom;
}
}
ポップアップ処理は以上です。
アプリの実行
以上で、

F12 開発者ツールを使えば、


図では、
実行するとわかるように、
if (map.getZoom() < 10) {
pins.clear();
return;
}
地理情報以外の検索条件
これまで地理情報
まず、
- $filter=Code eq 1
Codeが1または2の場合は
- $filter=Code eq 1 Or Code eq 2
となります。文字列の場合は、
- $filter=Address eq '岩手県大船渡市末崎町字峰岸XXX-X'
のようにシングルクォーテーションを使って記述します。これらの仕様はODataのFilter System Query Optionに基づいています。ただし、
演算子 | 説明 |
---|---|
Logical Operators | |
Eq | イコール、 |
Ne | ノットイコール、 |
Gt | 大なり |
Ge | 大なりイコール |
Lt | 小なり |
Le | 小なりイコール |
And | 論理積 |
Or | 論理和 |
Not | 否定 |
Grouping Operators | |
() | 括弧による優先順位の指定 例: |
ガソリンスタンド アプリに、
data: {
spatialFilter: bbox,
key: key,
$top: 250,
$filter: "Code eq 1",
$format: "json"
},
プロパティによる検索
プロパティの値のみを検索の条件にもできます。その場合のリクエストURLは次のようになります。
- http://
spatial. virtualearth. net/ REST/ v1/ data/accessId/dataSourceName/entityTypeName?
$filter=filterString&
queryoption1&
queryoption2&
:
queryoptionN&
jsonp=jsonCallBackFunction&
jsonso=jsonState&
key=queryKey
地理情報によるデータ検索のときと比べて、
IDによる検索
IDの値を条件とする場合は、
- http://
spatial. virtualearth. net/ REST/ v1/ data/accessId/dataSourceName/entityTypeName(entityId)?
jsonp=jsonCallBackFunction&
jsonso=jsonState&
key=queryKey
entityIdが、
IDの検索の場合、
複数のIDを一度に指定する場合は次のようになります。
- http://
spatial. virtualearth. net/ REST/ v1/ data/accessId/dataSourceName/entityTypeName?
$filter=entityId in (entityId1,entityId2,entityIdN)&
queryoption1&
queryoption2&
queryoptionN&
jsonp=jsonCallBackFunction&
jsonso=jsonState&
key=queryKey
複数の指定の場合は、
- $filter=FuelStores in ('03-n0005',' 23-n00597')
というように指定し、
ヒートマップの表示
おまけとして、
Alastair Aitchisonからライブラリーのzipファイルをダウンロードします。そして、
<script type="text/javascript" src="heatmap.js"></script>
使い方は非常に簡単です。ガソリンスタンド アプリでは、
まず、
var heatMapLayer = new HeatMapLayer(map, []);
そして、
success: function (data, dataType) {
//addPins(data);
showHeatMap(data);
}
showHeatMap関数の内容は次のように記述します。取得したデータからLocationオブジェクトの配列を作成し、
function showHeatMap(data) {
if (!data || !data.d || !data.d.results) {
return;
}
var locs = [];
for (var i = 0; i < data.d.results.length; ++i) {
var store = data.d.results[i];
var loc = new Microsoft.Maps.Location(store.Latitude, store.Longitude);
locs.push(loc);
}
heatMapLayer.SetPoints(locs);
}
以上で、
ライブラリー自体はもう少し機能があります。詳しくはサンプルやBlogを参照してください。また、
今回は以上です。Bing Maps Data ServicesとBing Maps AJAX Controlを組み合わせた簡単なアプリを作ってみました。いかがでしたでしょうか。次回もBing Maps Data Servicesについてお届けする予定です。