Linked Open Data (LOD)のデータセットを利用して、junaioのロケーション・ベースのAR(Location-based Augmented Reality)コンテンツを、Hello Worldしてみた。
題材は、北海道の山と函館の観光コース。データは、LODチャレンジなどでおなじみのDBpedia Japanese、函館まちあるきルート情報LOD、函館映画ロケ地情報LODと函館土木遺産情報LODだ。
ARブラウザjunaioを提供するmetaio社は、PHPを使ったクイック・スタートを紹介しているし、サーバー(channel)構築用にPHPのヘルパー・ライブラリーを提供している。気がついたら、Google App EngineでPHPを使えるようになっていて(PHP 5.4)、自分でサーバーを立てなくても、ARコンテンツ開発を試せるようになっていた。そこで、PHPの勉強がてら、junaioのARコンテンツ作りを試してみることに。結局、metaio社のライブラリーは、まだ使っていないが。
junaioを使ったARシステムの基本データ・フローは、metaio社のサイト、例えば「How channels work」に載っている。その図に、今回の、データ元のLODデータセットを使ったHello Worldフローを追記すると次のようになる。
LODデータセット
↓ (0.2) 結果をJSONで返す
↑ (0.1) SPARQLで問い合わせる ← このPHPスクリプト
│ (0.3) 結果のJSONを保存する
│ (0.4) 保存したJSONからAREL XMLを作る ← このPHPスクリプト
Developer channel hosting servers
↓ (3) 保存しておいたAREL XMLを返す
↑ (2) channel xyのコンテンツURLにHTTPリクエスト
junaio backend
↓ (4) AREL XMLを返す
↑ (1) channel xyのコンテンツをリクエスト
junaioブラウザー
図1 データフロー
DBpedia Japaneseから「北海道の山」情報をゲット (0.1)
DBpedia JapaneseからSPARQLを使って、北海道の山の、緯度・経度・標高を取得する、図1 (0.1)のPHPスクリプトはこんな感じ:
<?php
$query = <<< QUERY
PREFIX geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX dcterms: <http://purl.org/dc/terms/>
select distinct * where {
{
?s geo:lat ?lat;
geo:long ?long;
rdfs:label ?label;
<http://ja.dbpedia.org/property/標高> ?alt;
dcterms:subject <http://ja.dbpedia.org/resource/Category:北海道の山>;
rdfs:comment ?comment;
<http://xmlns.com/foaf/0.1/isPrimaryTopicOf> ?topicOf;
<http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Mountain> .
}
UNION
{
?s rdfs:label ?label;
<http://ja.dbpedia.org/property/標高> ?alt;
dcterms:subject <http://ja.dbpedia.org/resource/Category:北海道の山>;
rdfs:comment ?comment;
<http://xmlns.com/foaf/0.1/isPrimaryTopicOf> ?topicOf;
<http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Mountain> ;
<http://dbpedia.org/ontology/wikiPageExternalLink> ?wcz .
FILTER regex( ?wcz, "http://watchizu.gsi.go.jp/watchizu.html", "i" )
}
}
QUERY;
$endpoint = 'http://ja.dbpedia.org/sparql';
$format = 'application/sparql-results+json';
$others = '&timeout=0&debug=on&default-graph-uri=http%3A%2F%2Fja.dbpedia.org';
$iri = $endpoint
. '?' . 'query=' . urlencode($query)
. '&format=' . urlencode($format)
. $others;
$file = @file_get_contents($iri, true);
if ($file === FALSE) {
die('Error in reading "' . $iri . '"');
} else {
header('Content-type: application/json; charset=utf-8');
print $file;
}
山の緯度(geo:lat)・経度(geo:long)は設定されていなくても国土地理院「ウォッちず」へのリンクが設定されている山(の記事)がある。リンクのパラメタに緯度・経度が含まれているので、後で取り出すことにする。
タイムアウトなどしなければ、結果をJSON形式で受け取ることができる。それを/mt_kita.json
ファイルに保存しておくとする。
「北海道の山」AREL XMLの作成 (0.4)
保存しておいた、JSON形式の/mt_kita.json
を読み込んでAREL形式のXMLファイルを作成する、図1 (0.4)のPHPスクリプトはこれ:
<?php
header('Content-type: application/xml');
$iri = '/mt_kita.json';
$file = @file_get_contents($iri, true);
if ($file === FALSE) {
die('Error in reading "' . $iri . '"');
}
$json_dec = json_decode($file);
$results_bindings = $json_dec->results->bindings;
$object_id = 0;
/*
* character referenceにしないと、junaioのvalidatorが誤動作するらしい。
*/
$convmap = array(0, 0x10FFFF, 0, 0x10FFFF);
$button_name = "Wikipedia"
. mb_encode_numericentity("の記事", $convmap, 'utf-8', TRUE);
print <<< ARELOPEN
<?xml version="1.0" encoding="UTF-8"?>
<results trackingurl="GPS">
<arel><![CDATA[arel.html]]></arel>
ARELOPEN;
foreach ($results_bindings as $mt) {
$object_id += 1;
$object_id_str = 'oid' . $object_id;
$button_id_str = 'button' . $object_id;
$dirname = dirname($mt->topicOf->value);
$basename = basename($mt->topicOf->value);
$basename_enc = urlencode($basename);
if ($mt->lat) {
# おかしな値が目立つように、仮に6桁にまるめておく。
$lat = round($mt->lat->value, 6);
$lon = round($mt->long->value, 6);
} elseif ($mt->wcz) {
# ウォッちずへのリンクを解析して緯度・経度を取り出す。
$url = parse_url($mt->wcz->value)["query"];
parse_str($url, $param);
$longitude = $param["longitude"];
$latitude = $param["latitude"];
$b = $param["b"];
$l = $param["l"];
if ($longitude) {
$lat = round($latitude, 6);
$lon = round($longitude, 6);
} elseif ($b) {
# 「44.8074.3」といった値が入ってることがある。ここでは処理をサボる。
$b = floor($b);
# 44.8074は「44度80分74秒」
$b_sec = mb_substr($b, -2);
$b_min = mb_substr($b, -4, 2);
$b_deg = mb_substr($b, 0, mb_strlen($b) - 4);
$b_lat = round($b_deg + $b_min / 60 + $b_sec / 3600, 6);
$lat = $b_lat;
$l = floor($l);
$l_sec = mb_substr($l, -2);
$l_min = mb_substr($l, -4, 2);
$l_deg = mb_substr($l, 0, mb_strlen($l) - 4);
$l_lon = round($l_deg + $l_min / 60 + $l_sec / 3600, 6);
$lon = $l_lon;
} else {
}
} else {
}
# 「大有珠:737」といった値が入ってることがある。
if (is_numeric($mt->alt->value)) {
/*
* junaioのvalidatorが、alt要素のtypeは'xs:integer'だと言うので、
* $mt->alt->valueの値を整数にまるめる。
* でもAREL XML referenceには<alt>[FLOAT: WGS84]</alt>とある。
* http://dev.metaio.com/arel/xml-reference/
*/
$alt = "<alt>" . round($mt->alt->value, 0) . "</alt>";
$alt_desc = number_format($mt->alt->value);
} else {
/*
* junaioのvalidatorが、alt要素は必須だと言うので、標高0mと設定しておく。
*/
$alt = "<alt>0</alt>";
$alt_desc = "-";
}
print <<<EOF
<object id="{$object_id_str}">
<title><![CDATA[{$mt->label->value}]]></title>
<thumbnail><![CDATA[thumb.png]]></thumbnail>
<icon><![CDATA[icon.png]]></icon>
<location>
<lat>{$lat}</lat>
<lon>{$lon}</lon>
{$alt}
</location>
<popup>
<description>
<![CDATA[{$mt->comment->value}
緯度: {$lat}
経度: {$lon}
標高: {$alt_desc}
このARELはDBpedia Japaneseを利用しています。
DBpedia Japanese by DBpedia Community
( http://ja.dbpedia.org/ )
Powered by Linked Open Data (LOD)]]></description>
<buttons>
<button id="{$button_id_str}" name="{$button_name}"><![CDATA[{$dirname}/{$basename_enc}]]></button>
</buttons>
</popup>
</object>
EOF;
}
print <<< ARELCLOSE
</results>
ARELCLOSE;
この出力を保存しておいて、図1の(3)で返せばよい。http://example.com/mt_kita.xml
というURLを持たせたとすると、junaioのチャネルのContent server URLにこのURLを設定すればよい。
これで、ARアプリケーションができた。
函館観光情報をゲット
ARコンテンツを、函館の観光情報から作ってみる。使用するのは、函館まちあるきルート情報LOD、函館映画ロケ地情報LODと函館土木遺産情報LOD。図1の(0.1)にあたる処理。
<?php
$query = <<< QUERY
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX scm: <http://schema.org/>
SELECT * WHERE {
?s <http://www.w3.org/2003/01/geo/wgs84_pos#long> ?long;
<http://www.w3.org/2003/01/geo/wgs84_pos#lat> ?lat .
OPTIONAL { ?s scm:image ?image }
OPTIONAL { ?s rdfs:label ?label }
OPTIONAL { ?s scm:name ?name }
OPTIONAL { ?s <http://purl.org/dc/terms/description> ?description }
OPTIONAL { ?s scm:url ?url }
}
QUERY;
$endpoint = 'http://210.226.0.93/sparql/';
$format = 'json';
$others = '';
$iri = $endpoint
. '?' . 'query=' . urlencode($query)
. '&output=' . urlencode($format)
. $others;
$file = @file_get_contents($iri, true);
if ($file === FALSE) {
die('Error in reading "' . $iri . '"');
} else {
header('Content-type: application/json; charset=utf-8');
print $file;
}
この出力を"/hakodate.json"に保存しておくとする。
函館観光情報のAREL XMLを作成
"/hakodate.json"からデータを読み込んで、ARコンテンツを出力する。図1の(0.4)にあたる処理。
<?php
$iri = '/hakodate.json';
$file = @file_get_contents($iri, true);
if ($file === FALSE) {
die('Error in reading "' . $iri . '"');
}
$json_dec = json_decode($file);
if ($json_dec === NULL) {
die('Fail to docode json file: '
. $iri
. "\n" . json_last_error()
);
}
if (!(is_object($json_dec))) {
die('Error. json file ' . $iri . ' decoded but not object.');
}
$results_bindings = $json_dec->results->bindings;
$object_id = 0;
header('Content-type: application/xml');
/*
* character referenceにしないと、junaioのvalidatorが誤動作するらしい。
*/
$convmap = array(0, 0x10FFFF, 0, 0x10FFFF);
$button_name = mb_encode_numericentity("記事", $convmap, 'utf-8', TRUE);
print <<< ARELOPEN
<?xml version="1.0" encoding="UTF-8"?>
<results trackingurl="GPS">
<arel>arel.html</arel>
ARELOPEN;
foreach ($results_bindings as $mt) {
$object_id += 1;
$dirname = dirname($mt->topicOf->value);
$basename = basename($mt->topicOf->value);
$basename_enc = urlencode($basename);
if ($mt->label->value) {
$name = $mt->label->value;
} elseif ($mt->name->value) {
$name = $mt->name->value;
} else {
$name = "(no name)";
}
if ($mt->lat->value) {
# print $mt->s->value . "," . $mt->lat->value . "\n";
$lat = round($mt->lat->value, 6);
$lon = round($mt->long->value, 6);
} else {
}
if ($mt->url->value) {
/*
* junaioのvalidatorが、buttons要素の子供にbutton要素が必須だと言うので、
* buttonが作れるときだけbuttons要素を作る。
*/
$button_desc = "<buttons><button id=\"url\" name=\""
. $button_name
. "\"><![CDATA[{$mt->url->value}]]></button></buttons>";
} else {
$button_desc = "";
}
print <<<ARELOBJECT
<object id="{$object_id}">
<thumbnail><![CDATA[thumb.png]]></thumbnail>
<icon><![CDATA[icon.png]]></icon>
<title><![CDATA[{$name}]]></title>
<popup>
<description><![CDATA[{$mt->description->value}
緯度: {$lat}
経度: {$lon}
このARELは次のLinked Open Data (LOD)を利用しています:
函館まちあるきルート情報 by 公立はこだて未来大学 高度ICT演習観光系プロジェクト
函館映画ロケ地情報 by 公立はこだて未来大学 高度ICT演習観光系プロジェクト
)函館土木遺産情報 by 公立はこだて未来大学 高度ICT演習観光系プロジェクト]]></description>
{$button_desc}
</popup>
<location>
<lat>{$lat}</lat>
<lon>{$lon}</lon>
<alt>0</alt>
</location>
</object>
ARELOBJECT;
}
print <<< ARELCLOSE
</results>
ARELCLOSE;
この出力を保存しておいて、図の(3)で返せばよい。それで、ARアプリケーションを手作業で作ったことになる。
2014-05-30 改訂1
juaioのvalidatorが出すwarningに対処するため、AREL XMLの一部を文字参照に置き換えた。
コメント
コメントフィードを購読すればディスカッションを追いかけることができます。