使用例と使い方
ロケーションARは開発者にGeo(位置認識)マーカーに双方向で使いやすいデジタルコンテンツを付けることを可能にします。
引き続きGeo ARの使い方を学ぶことは、その技術と生い立ちを検証し、無料のSDKトライアルにアクセスし、そして、Geo AR の参考事例を見ることができます。
Geo AR 事例
WikitudeはARの開発を2008年から続けてきました。そして、世界で最初の歩行者と車のナビゲ―ションシステムを完成させました。それは、AR画面を統合し、地図への依存をなくしたものでした。
10年以上前に完成させたそれは、ナビゲーションとガイダンスの世界で、将来の革新的な段階に進む、として何度も賞賛されました。
1901年にその最初の概念的な考え方が発表されたARは、きわめて短い間に広まったけれども、ロケーションARベースのモバイルゲームはそのテクノロジーを確立したのは2016年でした。
ぽけもんGOを聞いたことがあるでしょう?
ぽけもんGOはARの世界でビッグヒットを飛ばしただけではなく、今までに制作されたモバイルゲームの中で、実際に最も成功した物の一つです。
世界中のプレイヤーが、彼らの周りを徘徊しながら、興味のある特定の場所に張り付けられたデジタル化されたポケモンを発見するアプリを使います。ぽけもんGO Geo アプリは驚異的に記録を塗り替えました。発表されてわずか20日で1億ドルの売上を達成しました。
ポケモンGOの成功はGeo AR ゲームとロケーションベースのAR の一般的な使用の世界に扉を開き、多くの投資に動機付けを与えました。これがGeoベースのARアプリが多くの分野で見られるようになった理由です。分野は、エンターテイメント、ナビゲ―ション、小売業、観光、通信、不動産、等です。
Geo AR ゲームのアイデアと必要性は始めるための後押しになるでしょうか?簡単な3つのステップでぽけもんGOのようなアプリをどのように作るのかを学びましょう。
Geo AR:ロケーションARの技術
Geo AR 技術は、開発者に興味のある地点へデジタルコンテンツを加えることを許してきました。このことは、典型的なマーカーAR機能、イメージトラッキング(画像認識)やオブジェクトトラッキング(対象物認識)などとは異なっていることを意味しています。Geo ARは物理的なターゲットをAR実行のトリガーとして必要としないのです。
拡張現実は、特定の事前に決めた位置に張り付けられて、その地点で現れます。
スマート機器のユーザーは、3D拡張現実、ビデオ、文章、音声、リンク等の各種のコンテンツを見たり、やり取りをするために、地理的な場所をスキャンすることができます。
Wikitude AR SDKは、地理的な参照データ出作動する使いやすい機能包含しています。実例によると、センサーベースのロケーショントラッキングはGPS、コンパス、そして加速度計やネットワーク、またはビーコンを通して作動します。
Wikitude AR SDK(無料体験版)のダウンロード
Geo ARを始めるための最善の方法は無料体験版をダウンロードすることです。
Wikitude Geo ARはiOSとAndroidをサポートしています。言語は、Java,JavaScript,PhoneGap,Xamarin,External LBAR Unity, Plugin,機器はFlutter,Epson,とVuzixです。
Geo ARの作成方法:サンプル・インストラクション
Wikitud SDKをダウンロードしたら、サンプル・インストラクションがどのように作成し、特定の地点にマーカーを置くのかを説明してくれます。
サンプルは4つの異なった相互に連携したセクションに分かれています。シリーズの最後で、タイトル、説明、スムースにあるものから別のものへの移動を図示する選択した予備の注意書きを書いた、完璧な、再使用可能なマーカーを得ます。
Geo AR (Point of Interest)
POIの例はどのように特定の地点のマーカーを作成できるのかが示されています。
ロケーションでのPOI
最初の章は地点の画像を表示します。そのために、現在位置を得るために呼び出す、AR.context.onLocationChanged()
を使用します。位置が得られたら、それを AR.ImageDrawable
に置きます。
すべてのJavaScriptコードはpoiatlocation.js
.ファイルの中で見つけることができます。
すでにImage Recognition の例は、ARを表示する中で、どのようにイメージ(画像)が配置され、表示されるかが説明されています。このサンプルは、World variableが決定された時にAR.ImageResource
をロードします。それは、後に私達が作成しようとした時に、それぞれのマーカーのために再利用されます。
poiatlocation.jsの最後のラインは独自のAR.context.onLocationChanged
callbackをどのようにセットするのかを示しています。
AR.context.onLocationChanged = World.onLocationChanged;
独自の機能World.onLocationChangedは 機能がすでに呼びだされているか否かをフラグ World.initiallyLoadedData
でチェックします。また、AR.context.onLocationChangedをゼロにセットする可能性があります。この場合、機能はそれ以上呼び出せず、それ以降のロケーションのアップデートは受け付けられません。
World.onLocationChanged
の最初のcallで、Geo
情報を維持するObject(対象物)が生成されます。それは後にWorld.loadPoisFromJsonData機能で使うマーカーを生成するのに使われます。
locationChanged: function locationChangedFn(lat, lon, alt, acc) {
// request data if not already present
if (!World.initiallyLoadedData) {
var poiData = {
"id": 1,
"longitude": (lon + (Math.random() / 5 - 0.1)),
"latitude": (lat + (Math.random() / 5 - 0.1)),
"altitude": 100.0
};
World.loadPoisFromJsonData(poiData);
World.initiallyLoadedData = true;
}
}
loadPoisFromJsonData
機能は後にImage(画像)として使われる AR.ImageResource
を生成します。
// start loading marker assets
World.markerDrawable_idle = new AR.ImageResource("assets/marker_idle.png");
マーカーを生成するために、新しい対象物(Object)AR.GeoObject
が特定の地点に生成されます。AR.GeoObjectは
複数の AR.Drawables
で一つかそれ以上のAR.GeoLocationsを接続します。
AR.Drawablesは複数のターゲットを定義することができます。ターゲットはカメラ、レーダー、または方向指示器で指示されます。レーダーと方向指示器の両方は後のサンプルでより細部までカバーしています。
// create the marker
var markerLocation = new AR.GeoLocation(poiData.latitude, poiData.longitude, poiData.altitude);
var markerImageDrawable_idle = new AR.ImageDrawable(markerDrawable_idle, 2.5, {
zOrder: 0,
opacity: 1.0
});
// create GeoObject
var markerObject = new AR.GeoObject(markerLocation, {
drawables: {
cam: [markerImageDrawable_idle]
}
});
最終的にユーザーフィードバックとしてステータスメッセージがアップデートされます。それで全てがきちんとロードされたことを示します。
World.updateStatusMessage('1 place loaded');
POI with Label
2番目の章はマーカーオブジェクトにタイトルと説明ラベルを加えます。そしてもっと引き付ける関係するオプションで覆います。
すべてのJavaScriptの変更はpoiwithlabel.jsです。
ファイルだけが名前が変更されますがコンテンツはpoiatlocation.jsとほとんど同じだということに留意してください。
locationChanged
機能が記述とタイトルをマーカーに加えます。
var poiData = {
"id": 1,
"longitude": (lon + (Math.random() / 5 - 0.1)),
"latitude": (lat + (Math.random() / 5 - 0.1)),
"altitude": 100.0,
"description": "This is the description of POI#1",
"title": "POI#1"
};
マーカーに関する追加の変更がおきたら、Marker
class
(see marker.js).を分けるためにコードを抜きとることがいいやり方です。コードの部品はloadPoisFromJsonDataからMarker
class: the
creation of the AR.GeoLocation
,
the creation of the AR.ImageDrawable
そして
the creation of the AR.GeoObject
.へ移動します。そうして、 loadPoisFromJsonData
機能の中のMarkerのインスタンスを生成します。
// create the marker
var marker = new Marker(poiData);
同じ場所で複数のAR.Drawablesを引き出す間、考えなければならない二つの重要な点があります。どちらを後にまたは背後に引きだすかを定義しなければなりません(Rendering order)、そして、それらはロケーションをやり直す必要があるかどうかを定義しなければなりません。この二つのシナリオのために、ARchitectは引きだしの操作を調節するいくつかの機能を持っています。
背景の前面にAR.Labelの位置を決めるため、引き出し可能な背景(AR.ImageDrawable2D
)は zOrder
of
0を受け取ります。両方のラベルはzOrder
of
1を持ちます。このやり方で、ラベルが引き出し可能な背景の前面に引き出されることを保証します。
確認された両方のラベルは、重なり合った同じAR.GeoObject
で接続された、同じ位置の上に引き出されます。それらの位置の調整は、 AR.Drawable
Objectが持っている translate.x
とtranslate.yを変更します。移行のための単位はSDUs(Size
and Distances)で決めます。詳細はSDUsの章をご覧ください。
次に両方のAR.Labelsはイニシャライズと位置決めがされます。AR.ImageDrawable
.と同じ要領でAR.GeoObject
のcamプロパティに追加されます。
function Marker(poiData) {
this.poiData = poiData;
var markerLocation = new AR.GeoLocation(poiData.latitude, poiData.longitude, poiData.altitude);
this.markerDrawable_idle = new AR.ImageDrawable(World.markerDrawable_idle, 2.5, {
zOrder: 0,
opacity: 1.0
});
this.titleLabel = new AR.Label(poiData.title.trunc(10), 1, {
zOrder: 1,
translate: {
y: 0.55
},
style: {
textColor: '#FFFFFF',
fontStyle: AR.CONST.FONT_STYLE.BOLD
}
});
this.descriptionLabel = new AR.Label(poiData.description.trunc(15), 0.8, {
zOrder: 1,
translate: {
y: -0.55
},
style: {
textColor: '#FFFFFF'
}
});
// Changed:
this.markerObject = new AR.GeoObject(markerLocation, {
drawables: {
cam: [this.markerDrawable_idle, this.titleLabel, this.descriptionLabel]
}
});
return this;
}
追加として、与えられた長さより長い文章を切り捨てる機能を延長します。この機能は短くなりすぎるタイトルや文章に使われます。
String.prototype.trunc = function(n) {
return this.substr(0, n - 1) + (this.length > n ? '...' : '');
};
Multiple POIs
3番目の事例は2つの部分から成り立っています。最初の章はいかにして複数のマーカーを生成するかということで、2番目の章はマーカー選択の準備を記述しています。
複数のマーカーを生成するために、class World
.を変更します。異なったPOIデータを生成するために使われるパラメーターとしての位置情報(緯度、経度)がついたrequestDataFromLocal機能をユーザーの近くのランダムなロケーションへ追加します。新機能は今までの事例で使ったloadPoisFromJsonData
を呼び出す代わりにlocationChanged
から呼び出されます。
World.requestDataFromLocal(lat, lon);
POIデータが生成された後は、loadPoisFromJsonData機能は新機能 requestDataFromLocal
の中から呼び出されます。
// request POI data
requestDataFromLocal: function requestDataFromLocalFn(centerPointLatitude, centerPointLongitude) {
var poisToCreate = 20;
var poiData = [];
for (var i = 0; i < poisToCreate; i++) {
poiData.push({
"id": (i + 1),
"longitude": (centerPointLongitude + (Math.random() / 5 - 0.1)),
"latitude": (centerPointLatitude + (Math.random() / 5 - 0.1)),
"description": ("This is the description of POI#" + (i + 1)),
"altitude": "100.0",
"name": ("POI#" + (i + 1))
});
}
World.loadPoisFromJsonData(poiData);
}
loadPoisFromJsonData
の拡張現実を、何かの適合性を必要とする前に単独のObject
(対象物)ではなく並列として使ったので、loadPoisFromJsonData機能の中で拡張現実として配布される並列POI情報は、poiData objectsを生成するために使われます。すべてのpoi情報Objectsの中をループを繰り返し、それぞれのObjectは新ObjectsinglePoiを生成します。 複数のマーカーを生成するために、new
Marker(poiData)
は、poiData
Objectの中で定義されたように、違う場所、タイトル、そして文章で何度も呼び出すことができます。そうして、Marker Objedtsを生成し、それらを並列のmarkerList蓄積します。それはWorld
classの中に変更可能なメンバーとして定義されます。markerList
arrayは、マーカーの選択/選択解除のために必要です。そして、後でこの事例の中に記述されています。最終的にステイタス・メッセージはPOIがロードされた番号で更新されます。
// called to inject new POI data
loadPoisFromJsonData: function loadPoisFromJsonDataFn(poiData) {
// empty list of visible markers
World.markerList = [];
// start loading marker assets
World.markerDrawable_idle = new AR.ImageResource("assets/marker_idle.png");
// loop through POI-information and create an AR.GeoObject (=Marker) per POI
for (var currentPlaceNr = 0; currentPlaceNr < poiData.length; currentPlaceNr++) {
var singlePoi = {
"id": poiData[currentPlaceNr].id,
"latitude": parseFloat(poiData[currentPlaceNr].latitude),
"longitude": parseFloat(poiData[currentPlaceNr].longitude),
"altitude": parseFloat(poiData[currentPlaceNr].altitude),
"title": poiData[currentPlaceNr].name,
"description": poiData[currentPlaceNr].description
};
World.markerList.push(new Marker(singlePoi));
}
World.updateStatusMessage(currentPlaceNr + ' places loaded');
}
ここまでで、複数のマーカーを表示する準備は終わりました。それでは、選択されたマーカーの背景画像が、どの様に変わったかを見てみましょう。そして、別の選択した状態を操作してみましょう。
2番目のAR.ImageDrawable
はmarker.js
.の中に定義されています。
ユーザーの反応に対応するために、onClickプロパティがそれぞれのAR.Drawable 用にセット可能です。プロパティは毎回ユーザーがdrawableの上でタップする時に呼び出される機能です。以下の断片は修正された AR.ImageDrawable
生成を示しています。
this.markerDrawable_idle = new AR.ImageDrawable(World.markerDrawable_idle, 2.5, {
zOrder: 0,
opacity: 1.0,
onClick: Marker.prototype.getOnClickTrigger(this)
});
それぞれのタップを呼び出す機能は、marker.js
.の中で定義されている次のヘルプ機能から呼び戻されます。この機能は、変更可能なisSelectedのヘルプを持った選択した状況をチェックする機能です。そして適切な機能を実行します。クリックされたマーカーは拡張現実として受け渡されます。
Marker.prototype.getOnClickTrigger = function(marker) {
return function() {
if (marker.isSelected) {
Marker.prototype.setDeselected(marker);
} else {
Marker.prototype.setSelected(marker);
try {
World.onMarkerSelected(marker);
} catch (err) {
alert(err);
}
}
};
};
setSelectedとsetDeselected機能はMarker 機能のプロトタイプです。
両方の機能は同じ段取りで、しかし真逆に稼働します、これゆえに、唯一の機能 (setSelected
) が詳細をカバーしています。マーカーを選ぶには3つのステップが必要です。最初は、状況が適切にセットされることです。2番目は引き出される背景が可能であること、そして標準の背景は不可能であること。これは可視される状況の1.0と可視されない0.0のあいまいなプロパティをセットすることで成されます。3番目は、選択されたマーカーの引き出し可能な背景のためにだけセットされるonClick
機能です。
Marker.prototype.setSelected = function(marker) {
marker.isSelected = true;
marker.markerDrawable_idle.opacity = 0.0;
marker.markerDrawable_selected.opacity = 1.0;
marker.markerDrawable_idle.onClick = null;
marker.markerDrawable_selected.onClick = Marker.prototype.getOnClickTrigger(marker);
};
ユーザーが空のスクリーンをタップした時、マーカーの選択を解除することができるようにするために、World Objectは、それぞれのマーカーを保持している並列Objectsをつかみます。
World.markerList.push(new Marker(singlePoi));
引きだせないとなったクリックの場所を見つけるために、AR.context.に独自の機能をセットします。onScreenClickは現在選択されているマーカーが選択解除されている所です。
onScreenClick: function onScreenClickFn() {
if (World.currentMarker) {
World.currentMarker.setDeselected(World.currentMarker);
}
}
Selecting POIs
最後の章はAR.PropertyAnimationsと AR.AnimationGroups
.の背後にあるコンセプトを記述します。それは、また、如何に方向指示器がその時点では視野の中に入っていない選択したObjectを可視化するために使われるのか、を説明します。
AR.PropertyAnimationsによって、ほとんどどんなARchitect Objects のプロパティもアニメーション化することができます。このサンプルは両方の引き出し可能な背景を不透明なアニメーションにします。それにより片方は消え、片方は現れています。大きさも同じくアニメーション化されます。マーカーのサイズは時間切れで変更になります、そのためラベルは引き出し可能な背景に比較して比率を保つようにアニメーション化される必要があります。AR.AnimationGroupsは同時にまたは順番にすべてのアニメーションを同期するために使われます。
marker.jsの中で、2つの新しい変数が宣言されます。それらはアニメーションのスタートまたはストップのどちらかに使われる AR.AnimationGroup
へのリファレンスを持っています。
this.animationGroup_idle = null;
this.animationGroup_selected = null;
marker.jsの中の機能setSelected
とsetDeselectedは直されなければなりません。再度、setSelected
の中の変更だけを説明します。
AR.AnimationGroups
.には2つのタイプがあります。並列のアニメーションは同時に動きます。順番に沿ったアニメーションはいつが終わって次が動きます。このサンプルは並列のAR.AnimationGroup
.を使います。
if (marker.animationGroup_selected === null) {
var hideIdleDrawableAnimation = new AR.PropertyAnimation(marker.markerDrawable_idle, "opacity", null, 0.0, kMarker_AnimationDuration_ChangeDrawable);
var showSelectedDrawableAnimation = new AR.PropertyAnimation(marker.markerDrawable_selected, "opacity", null, 0.8, kMarker_AnimationDuration_ChangeDrawable);
var idleDrawableResizeAnimationX = new AR.PropertyAnimation(marker.markerDrawable_idle, 'scale.x', null, 1.2, kMarker_AnimationDuration_Resize, new AR.EasingCurve(AR.CONST.EASING_CURVE_TYPE.EASE_OUT_ELASTIC, {
amplitude: 2.0
}));
var selectedDrawableResizeAnimationX = new AR.PropertyAnimation(marker.markerDrawable_selected, 'scale.x', null, 1.2, kMarker_AnimationDuration_Resize, new AR.EasingCurve(AR.CONST.EASING_CURVE_TYPE.EASE_OUT_ELASTIC, {
amplitude: 2.0
}));
var titleLabelResizeAnimationX = new AR.PropertyAnimation(marker.titleLabel, 'scale.x', null, 1.2, kMarker_AnimationDuration_Resize, new AR.EasingCurve(AR.CONST.EASING_CURVE_TYPE.EASE_OUT_ELASTIC, {
amplitude: 2.0
}));
var descriptionLabelResizeAnimationX = new AR.PropertyAnimation(marker.descriptionLabel, 'scale.x', null, 1.2, kMarker_AnimationDuration_Resize, new AR.EasingCurve(AR.CONST.EASING_CURVE_TYPE.EASE_OUT_ELASTIC, {
amplitude: 2.0
}));
var idleDrawableResizeAnimationY = new AR.PropertyAnimation(marker.markerDrawable_idle, 'scale.y', null, 1.2, kMarker_AnimationDuration_Resize, new AR.EasingCurve(AR.CONST.EASING_CURVE_TYPE.EASE_OUT_ELASTIC, {
amplitude: 2.0
}));
var selectedDrawableResizeAnimationY = new AR.PropertyAnimation(marker.markerDrawable_selected, 'scale.y', null, 1.2, kMarker_AnimationDuration_Resize, new AR.EasingCurve(AR.CONST.EASING_CURVE_TYPE.EASE_OUT_ELASTIC, {
amplitude: 2.0
}));
var titleLabelResizeAnimationY = new AR.PropertyAnimation(marker.titleLabel, 'scale.y', null, 1.2, kMarker_AnimationDuration_Resize, new AR.EasingCurve(AR.CONST.EASING_CURVE_TYPE.EASE_OUT_ELASTIC, {
amplitude: 2.0
}));
var descriptionLabelResizeAnimationY = new AR.PropertyAnimation(marker.descriptionLabel, 'scale.y', null, 1.2, kMarker_AnimationDuration_Resize, new AR.EasingCurve(AR.CONST.EASING_CURVE_TYPE.EASE_OUT_ELASTIC, {
amplitude: 2.0
}));
marker.animationGroup_selected = new AR.AnimationGroup(AR.CONST.ANIMATION_GROUP_TYPE.PARALLEL, [hideIdleDrawableAnimation, showSelectedDrawableAnimation, idleDrawableResizeAnimationX, selectedDrawableResizeAnimationX, titleLabelResizeAnimationX, descriptionLabelResizeAnimationX,idleDrawableResizeAnimationY, selectedDrawableResizeAnimationY, titleLabelResizeAnimationY, descriptionLabelResizeAnimationY]);
}
スタート機能を使って AR.AnimationGroup
を搭載します。
marker.animationGroup_selected.start();
Marker.prototype.getOnClickTrigger機能の中で、選択機能は、アニメーション無しが稼働している時にだけ、呼び出されます。
if (!Marker.prototype.isAnyAnimationRunning(marker)) {
if (marker.isSelected) {
Marker.prototype.setDeselected(marker);
} else {
Marker.prototype.setSelected(marker);
try {
World.onMarkerSelected(marker);
} catch (err) {
alert(err);
}
}
} else {
AR.logger.debug('a animation is already running');
}
方向指示器のために表示される必要がある画像を参照するAR.ImageResource
を生成します。そうしてAR.ImageResource
.を使用してAR.ImageDrawable
を生成します。画像の移動と停止のためのオプションをセットします。そうすると、それが画面の端に正しく表示されます。
this.directionIndicatorDrawable = new AR.ImageDrawable(World.markerDrawable_directionIndicator, 0.5, {
enabled: false
});
最後の段階はマーカーAR.GeoObject
の上で、指示器またはターゲットとして、AR.ImageDrawable
を定義することです。方向指示器は必要がある時に自動的に表示されます。AR.Drawable
のサブクラス
(e.g. AR.Circle
)は方向指示器として使われます。
this.markerObject = new AR.GeoObject(markerLocation, {
drawables: {
cam: [ this.markerDrawable_idle,
this.markerDrawable_selected,
this.titleLabel,
this.descriptionLabel
],
indicator: this.directionIndicatorDrawable
}
});
Retrieving POI Data
ARchitect WorldにおけるPOIデジタル情報でリクエストと作業をするにはいくつかの方法があります。あなたのアプリケーションと事例によって、一つがもっともよくフィットするでしょう。
From Application Model
格納されているデータをロードしている横で、データベースからデータをロードすることもまた可能です。または、それをネイティブモードで生成することも可能です。あなたのデータのJSON Objectを生成するためには、プラットフォームの通常のやり方を使い、そして、それらをARchitect World's JavaScriptに渡すためにarchitectView.callJavaScript()を使います。
どのようにデータがJavaScriptに注入されるのかをよりよく理解するためにSampleCamContentFromNativeActivity.java
を見ます。
From a Local Resource
ARchitect Worldのあなたのデータが静的である場合、コンテンツはアプリケーションの中に蓄えられなければなりません。そこでは広くアクセスできる変数が定義されるJavaScriptファイル(e.g. myjsondata.js
)を生成します。
var myJsonData = …[YOUR-JSON-DATA]
あなたのJaveScriptのどこでも可能なところにPOI情報を作るために<script
src="js/myjsondata.js"/>
を加えることによってARchitect Worlds HTMLの中にJavaScriptを含めます。
// request POI data
requestDataFromLocal: function requestDataFromLocalFn(lat, lon) {
World.loadPoisFromJsonData(myJsonData);
}
注記:このサンプルは静的なPOIデータを使い、Helper.bringPlacesToUser
,を使った緯度、経度変数を書き替えています。これを削除するためにはこの行を移動させなければなりません。
From a Webservice
JQueryはデータをリモートの発生元から取りだす数々のツールを提供します。POI情報のためには、JSONフォーマットを使うことを特に推奨します。要求と構文解析はわずかな行のコードで成されます。
ロケーションのアップデートで発動される方式を定義するためには、e.g. AR.context.onLocationChanged
= World.locationChanged;
を使います。このサンプルでは、POI情報は大変に早いロケーション・アップデートの後に、要求されます。
注:World.locationChanged
.の中に、もうこれ以上ロケーションアップデートを受け付けないとわかった後に、AR.context.onLocationChanged
= nullをセットします。
サーバー情報は分離してストアーすることをお勧めします。
// holds server information
var ServerInformation = {
// sample service returning dummy POIs
POIDATA_SERVER: "http://example.wikitude.com/GetSamplePois/",
POIDATA_SERVER_ARG_LAT: "lat",
POIDATA_SERVER_ARG_LON: "lon",
POIDATA_SERVER_ARG_NR_POIS: "nrPois"
};
サーバーが有効なJSONに戻り、それが確実に抜けたことを確認します。(例えば、POInameの中の特別な文字など)。
サーバーの反応はWorld.loadPoisFromJsonData(poiData)へ送られます。そこは、マーカーの生成とそのカメラの再表示が定義されます。
// location updates
locationChanged: function locationChangedFn(lat, lon, alt, acc) {
/* Request data from server only once*/
if (!World.alreadyRequestedData) {
World.requestDataFromServer(lat, lon);
World.alreadyRequestedData = true;
}
},
// request POI data
requestDataFromServer: function requestDataFromServerFn(lat, lon) {
// set helper var to avoid requesting places while loading
World.isRequestingData = true;
World.updateStatusMessage('Requesting places from web-service');
// server-url to JSON content provider
var serverUrl = ServerInformation.POIDATA_SERVER + "?" + ServerInformation.POIDATA_SERVER_ARG_LAT + "=" + lat + "&" + ServerInformation.POIDATA_SERVER_ARG_LON + "=" + lon + "&" + ServerInformation.POIDATA_SERVER_ARG_NR_POIS + "=20";
var jqxhr = $.getJSON(serverUrl, function(data) {
World.loadPoisFromJsonData(data);
})
.error(function(err) {
World.updateStatusMessage("Invalid web-service response.", true);
World.isRequestingData = false;
})
.complete(function() {
World.isRequestingData = false;
});
}
Browsing POIs
カメラの中に数多くのPOIを表示することは挑戦です。どのくらいの数のPOIを出すべきなのでしょうか?同じ指示のPOIをどのように扱うのでしょうか?POIを表す最大範囲は何でしょうか?そしてどのように長い文を表示するのでしょうか?以下の事例は適切にPOI ブラウザーの事例に関して聞かれた質問をカバーしています。そして5章と1つの追加セクションで成り立っています。
Presenting Details(詳細表示)
POIは通常、名前と時には極めて長い記述を持っています。あなたのコンテンツのタイプによって、あなたはその名前と切り落とされた文章のマーカーを表示します。しかし、ユーザーにそれを選択した後で更なる情報を得ることを許しています。
jQuery Mobileはモバイル機器のための魅力的なユーザーインターフェイスを作成します。それは「書込み不要でもっとできる」を次のレベルへの御題目にしています。それぞれのモバイル機器、またはOSのために独自のアプリを書く代わりに、 jQueryのモバイルフレームワークは、あなたに、すべての普及したスマートホン、タブレット、そしてデスクトッププラットフォーム(彼らのWebSiteからの引用)の上で動く、一つの高度なブランド化されたWebSiteまたはアプリケーションをデザインさせます。私達は、拡張現実の表示のUIの構築のために jQuery mobileを使うことを推奨します。
それはよく文書化されており、アプリケーション、またはあなた自身のウエブサーバー上におかれた中に組み込むことさえできます。
このサンプルの中で、押されたcamマーカー(タイトルと文章の青色の箱)がサンプルの記録簿の中でindex.htmlと比較した時にPOI詳細パネルがあらわれます。
<!-- panel containing POI detail information -->
<div data-role="panel" id="panel-poidetail" data-position="right" data-display="overlay" style="background-color:#F0F0F0;" data-theme="c">
<!-- header with "close" button -->
<div data-role="header" data-theme="c">
<h1>Details</h1>
<a href="#header" data-rel="close">Close</a>
</div>
<!-- content of POI detail page, you may also add thumbnails etc. here if you like -->
<div data-role="content">
<!-- title -->
<h3 id="poi-detail-title"></h3>
<!-- description -->
<h4 id="poi-detail-description"></h4>
<!-- distance -->
<h4>Distance: <a id="poi-detail-distance"></a></h4>
</div>
マーカーを選んだ時、POI詳細divの中で現れたコンテンツはアップデートされます。同じく、パネルは右から左へアニメーションされます。
マーカーを選択取消するために、panelbeforecloseのイベントがpresentingPoiDetails.jsと比較して使われます。
onMarkerSelected: function onMarkerSelectedFn(marker) {
World.currentMarker = marker;
// update panel values
$("#poi-detail-title").html(marker.poiData.title);
$("#poi-detail-description").html(marker.poiData.description);
var distanceToUserValue = (marker.distanceToUser > 999) ? ((marker.distanceToUser / 1000).toFixed(2) + " km") : (Math.round(marker.distanceToUser) + " m");
$("#poi-detail-distance").html(distanceToUserValue);
// show panel
$("#panel-poidetail").panel("open", 123);
$("#panel-poidetail").on("panelbeforeclose", function(event, ui) {
World.currentMarker.setDeselected(World.currentMarker);
});
POI and AR Rader
ユーザーの近くでどこの場所にARが位置しているのかのヒントを与えることを推奨します。オリエンテーションを支援する、もっとも容易な方法はレーダーです。通常小さな点で指示します。
レーダーでのAR.GeoObject
の再表示はその引き出し可能なセット(AR.GeoObject
構築の2番目のAR)で定義されます。一旦、drawables.radarがセットされると、Objectはまた、レーダーの上にmarker.js:を比較してAR.Circle
,として現れます。
this.radarCircle = new AR.Circle(0.03, {
horizontalAnchor: AR.CONST.HORIZONTAL_ANCHOR.CENTER,
opacity: 0.8,
style: {
fillColor: "#ffffff"
}
});
this.radardrawables = [];
this.radardrawables.push(this.radarCircle);
選択した状態を表すために別の色で追加でサークルを生成するにはmarker.js:を比較します。
this.radarCircleSelected = new AR.Circle(0.05, {
horizontalAnchor: AR.CONST.HORIZONTAL_ANCHOR.CENTER,
opacity: 0.8,
style: {
fillColor: "#0066ff"
}
});
this.radardrawablesSelected = [];
this.radardrawablesSelected.push(this.radarCircleSelected);
this.markerObject = new AR.GeoObject(markerLocation, {
drawables: {
cam: [ this.markerDrawable_idle,
this.markerDrawable_selected,
this.titleLabel,
this.descriptionLabel ],
indicator: this.directionIndicatorDrawable,
radar: this.radardrawables
}
});
レーダーの中で選択したマーカーをハイライトするために、機能の中の引き出し可能機能をアップデートします。
Marker.prototype.setSelectedとMarker.prototype.setDeselected
,はmarker.js:を比較します。
marker.markerObject.drawables.radar = marker.radardrawablesSelected;
[...]
marker.markerObject.drawables.radar = marker.radardrawables;
レーダーの位置とそのサイズはDOM element.を使って定義されます。この事例の中でid radarContainer
がついた div
elementはindex.html:を比較します。
<div class="radarContainer_left" id="radarContainer"></div>
レーダーのサイズと位置はa css class,の中で定義されます。(poi-radar.css:参照)
/* position of POI-radar*/
.radarContainer_left {
position:absolute;
top:0px;
left:0px;
width:100px;
height:100px;
}
レーダーコンテナーを参照するエレメントの完全な位置を使うための助言をします。留意点:DOM エレメントが fly via jQuery上、またはresponsive design上でアップデートされたケースの場合、R.radar.notifyUpdateRadarPosition();を使います。レーダーの位置、サイズをアップデートせざるを得なく、一方で非常に早く位置、サイズが使用される時、AR.radar.notifyUpdateRadarPosition();を使用します。
レーダーそのものはカスタマイズできます。そして、radar.js.と比較して、JavaScript コードの中の分離されたコンポーネントとして用意しなければなりません。
var PoiRadar = {
hide: function hideFn() {
AR.radar.enabled = false;
},
show: function initFn() {
// the div defined in the index.htm
AR.radar.container = document.getElementById("radarContainer");
// set the back-ground image for the radar
AR.radar.background = new AR.ImageResource("assets/radar_bg.png");
// set the north-indicator image for the radar
// (not necessary if you don't want to display a north-indicator)
AR.radar.northIndicator.image = new AR.ImageResource("assets/radar_north.png");
// center of north indicator and radar-points in the radar asset,
// usually center of radar is in the exact middle of the background,
// meaning 50% X and 50% Y axis --> 0.5 for centerX/centerY
AR.radar.centerX = 0.5;
AR.radar.centerY = 0.5;
AR.radar.radius = 0.3;
AR.radar.northIndicator.radius = 0.0;
AR.radar.enabled = true;
},
updatePosition: function updatePositionFn() {
if (AR.radar.enabled) {
AR.radar.notifyUpdateRadarPosition();
}
},
// you may define some custom action when user pressed radar,
// e.g. display distance, custom filtering etc.
clickedRadar: function clickedRadarFn() {
alert("Radar Clicked");
},
setMaxDistance: function setMaxDistanceFn(maxDistanceMeters) {
AR.radar.maxDistance = maxDistanceMeters;
}
};
レーダーコンポーネントを起動するためにPoiRadar.show機能を呼び出します。もし、要求されたらaddingradar.js:と比較してクリックすることを定義するかもしれません。
// show radar & set click-listener
PoiRadar.show();
$('#radarContainer').unbind('click');
$("#radarContainer").click(PoiRadar.clickedRadar);
Limiting Visible POIs
ユーザーは時には、ただある決まった範囲のPOIを見ることだけに興味があるかもしれません。このサンプルはタイトルバーボタンの中にユーザーが興味の範囲を変更できるための追加ボタンを用意しています。
最初にタイトルバーの中にボタンを追加します。
index.html
<!-- header of UI holding feature buttons -->
<div id ="header-status" data-role="header" data-position="fixed" data-theme="c">
<a href="javascript: World.showRange();" data-icon="gear" data-inline="true" data-mini="true">Range</a>
<h1></h1>
</div>
そのあと、距離の範囲ためにパネルレイアウトを定義します。このケースではメートルでの現在の範囲と可視化されたPOIの数がパネルに表示されています。
index.html
<!-- range panel -->
<div data-role="panel" id="panel-distance" data-position="left" data-display="overlay" style="background-color:#F0F0F0;" data-theme="c">
<!-- header with close button -->
<div data-role="header" data-theme="c">
<h1>Range</h1>
<a href="#header" data-rel="close">Close</a>
</div>
<!-- distance information, calculated/updated in code -->
<div data-role="content">
<!-- Range in m/km-->
<h4> Range: <a id="panel-distance-value"></a></h4>
<!-- Amount of visible places -->
<h4> Visible: <a id="panel-distance-places"></a></h4>
<!-- default slider -->
<input id="panel-distance-range" type="range" data-highlight="true" name="rangeSlider" min="0" max="100" value="100" data-show-value="false" step="5" data-popup-enabled="false">
</div>
</div>
World.updateRangeValues
の機能はユーザーがこの変更数値を変えた場合には、いつでも実行されます。最大の距離の正しい計算と見える場所の合計の数値に加えて、AR.context.scene.cullingDistance
とPoiRadar.setMaxDistanceはレーダーの中のマーカーの描写とレーダーの中の引き出し機能のアップデートのためにlimitingrange.jsと比較して実行されます。
// updates values show in "range panel"
updateRangeValues: function updateRangeValuesFn() {
// get current slider value (0..100);
var slider_value = $("#panel-distance-range").val();
// max range relative to the maximum distance of all visible places
var maxRangeMeters = Math.round(World.getMaxDistance() * (slider_value / 100));
// range in meters including metric m/km
var maxRangeValue = (maxRangeMeters > 999) ? ((maxRangeMeters / 1000).toFixed(2) + " km") : (Math.round(maxRangeMeters) + " m");
// number of places within max-range
var placesInRange = World.getNumberOfVisiblePlacesInRange(maxRangeMeters);
// update UI labels accordingly
$("#panel-distance-value").html(maxRangeValue);
$("#panel-distance-places").html((placesInRange != 1) ? (placesInRange + " Places") : (placesInRange + " Place"));
// update culling distance, so only places within given range are rendered
AR.context.scene.cullingDistance = Math.max(maxRangeMeters, 1);
// update radar's maxDistance so radius of radar is updated too
PoiRadar.setMaxDistance(Math.max(maxRangeMeters, 1));
},
// returns number of places with same or lower distance than given range
getNumberOfVisiblePlacesInRange: function getNumberOfVisiblePlacesInRangeFn(maxRangeMeters) {
// sort markers by distance
World.markerList.sort(World.sortByDistanceSorting);
// loop through list and stop once a placemark is out of range ( -> very basic implementation )
for (var i = 0; i < World.markerList.length; i++) {
if (World.markerList[i].distanceToUser > maxRangeMeters) {
return i;
}
};
// in case no placemark is out of range -> all are visible
return World.markerList.length;
},
レーダーコンポーネントの位置は別のCSS スタイル(例えば、removeClassとaddClass
of
jQuery)を使い、PoiRadar.updatePosition()
を呼び出すことでアップデートできます。このサンプルでは、距離パネルが limitingrange.jsを比較した時にレーダーエレメントが右に動いています。
handlePanelMovements: function handlePanelMovementsFn() {
$("#panel-distance").on("panelclose", function(event, ui) {
$("#radarContainer").addClass("radarContainer_left");
$("#radarContainer").removeClass("radarContainer_right");
PoiRadar.updatePosition();
});
$("#panel-distance").on("panelopen", function(event, ui) {
$("#radarContainer").removeClass("radarContainer_left");
$("#radarContainer").addClass("radarContainer_right");
PoiRadar.updatePosition();
});
},
ユーザーが範囲ボタンを押した時、World.showRange
機能が実行されます。
// display range slider
showRange: function showRangeFn() {
if (World.markerList.length > 0) {
// update labels on every range movement
$('#panel-distance-range').change(function() {
World.updateRangeValues();
});
World.updateRangeValues();
World.handlePanelMovements();
// open panel
$("#panel-distance").trigger("updatelayout");
$("#panel-distance").panel("open", 1234);
} else {
// no places are visible, because the are not loaded yet
World.updateStatusMessage('No places available yet', true);
}
}
Reloading POI Data
ユーザーの動き、または色々な理由により手動で、POI情報を再ロードする必要があるかもしれません。この例では、POIはユーザーがリフレッシュボタンを押した時に再ロードされます。ボタンはindex.htmlの中に定義されています。そして、クリックでWorld.reloadPlaces()を呼び出します。
<a href="javascript: World.reloadPlaces()" data-icon="refresh" >Reload</a>
World.reloadPlaces()の準備はARchitect World(reloadingPois.js)の一部で、そして、ウエブサービスからユーザーの現在のロケーションによるデータを再取得するWorld.requestDataFromServerを実行します。
参考:確実な状況で、あなたのウエブサービスが稼働しない、または他の接続問題が発生します。接続問題についてユーザーに通知するために、ステータスメッセージがアップデートされます。あなた自身の準備の中で、あなたはinfo popupか similar.を使うことができます。
var World = {
[…]
// reload places from content source
reloadPlaces: function reloadPlacesFn() {
if (!World.isRequestingData) {
if (World.userLocation) {
World.requestDataFromServer(World.userLocation.latitude,
World.userLocation.longitude);
} else {
World.updateStatusMessage('Unknown user-location.', true);
}
} else {
World.updateStatusMessage('Already requesting places...', true);
}
}
[…]
}
Displaying Native Detail Screen
PIOの詳細をあなたの独自のスタイルで表示するのは良いと思います。このサンプルでは、ユーザーがHTMLでの'More' ボタンを押した時に大変にシンプルな独自のスクリーンを開きます。これはJavaScriptとnativeコードの間で相互に作用するデモをします。
皿にボタンがindex.htmlに追加されます。それはWorld.onPoiDetailMoreButtonClicked機能を呼び出します。
<!-- more button-->
<a href="javascript: World.onPoiDetailMoreButtonClicked();" data-role="button" data-icon="arrow-r" data-iconpos="right" data-inline="true">
More
</a>
World.onPoiDetailMoreButtonClicked
iはnativedetailscreen.jsの中に準備されます。そしてAR.platform.sendJSONObject(...)
.を実行します。独自のプロジェクトはこのコールに割り込みます。これは以下に示します。
var World = {
[…]
// user clicked "More" button in POI-detail panel -> fire event to open native screen
onPoiDetailMoreButtonClicked: function onPoiDetailMoreButtonClickedFn() {
var currentMarker = World.currentMarker;
var markerSelectedJSON = {
name: "markerselected",
id: currentMarker.poiData.id,
title: currentMarker.poiData.title,
description: currentMarker.poiData.description
};
AR.platform.sendJSONObject(markerSelectedJSON);
}
[…]
}
独自URL案ネイティブパートを記述するこのセクションthisを見てください。
ArchitectJavaScriptInterfaceListener
インターフェイスはJavaScript とnative
codeとの間の情報を交換することを許可します。それはnative
codeでJavaScriptからどこに コミュニケ―トしたいのか準備してあることが必要です。
onJSONObjectReceived(JSONObject
jsonObject)
の方式は AR.platform.sendJSONObjectがinvoked. A valid JavaScriptである時であればいつでも呼び出されます。
事例
public void onJSONObjectReceived(JSONObject jsonObject) {
try {
switch (jsonObject.getString("name")) {
case "markerselected":
final Intent poiDetailIntent = new Intent(SampleCamActivity.this, SamplePoiDetailActivity.class);
poiDetailIntent.putExtra(SamplePoiDetailActivity.EXTRAS_KEY_POI_ID, jsonObject.getString("id"));
poiDetailIntent.putExtra(SamplePoiDetailActivity.EXTRAS_KEY_POI_TITILE, jsonObject.getString("title"));
poiDetailIntent.putExtra(SamplePoiDetailActivity.EXTRAS_KEY_POI_DESCR, jsonObject.getString("description"));
SampleCamActivity.this.startActivity(poiDetailIntent);
break;
}
} catch (JSONException e) {
Log.e(TAG, "onJSONObjectReceived: ", e);
}
}
JavaScript の部分のさらに詳細が必要ならpresentingdetails.jsファイルを参照してください。
Capture Screen Bonus
このサンプルは友達とスナップショットをシェアーするためのcaptureScreen機能をどのよう使うのかを示します。JavaScriptとnative codeの間の相互作用のコンセプトはPOIデジタルパージのサンプルと同じですが、urlListener
は今回は代わりに画像シェアーを扱います。 "Snapshot"ボタンはタイトルバーの右上です。一旦現在のスクリーンをクリックすると捉えて、ユーザーにそれをシェアーすることを促します。
<!-- header of UI holding feature buttons -->
<div id ="header-status" data-role="header" data-position="fixed" data-theme="c">
<a href="javascript: World.showRange();" data-icon="gear" data-inline="true" data-mini="true">Range</a>
<a href="javascript: World.captureScreen()" data-icon="refresh" >Snapshot</a>
<h1></h1>
</div>
画像のシェアーは独自のコードで行われます。
// tell native (urlListener) that user pressed 'Snapshot' button
captureScreen: function captureScreenFn() {
AR.platform.sendJSONObject({
name: "button",
action: "captureScreen"
});
},
Solar System(Geo)
Solar System (IR) demoとよく似て、このデモは我々のsolar sytemの惑星を表示します。しかし、それらはロケーションベースのアプローチを使い、ユーザーの近くに位置します。
それぞれの惑星の詳細情報は、init()機能の中で定義されます。ファクターは、惑星を適切なサイズの大きさに定義し、そしてすべての惑星はplanetsInfo
アレイの中に結合されています。
/* put sun, planets (and pluto) in an array */
this.planetsInfo = [sun, mercury, venus, earth, mars, jupiter, saturn, uranus, neptun, pluto];
惑星は AR.GeoObject
によって表示されます。それは引き出し可能なように指定された惑星の画像と名前を特集しています。それぞれの惑星のAR.GeoObject
は、惑星のObjectがユーザーのロケーションに相対するようにするAR.RelativeLocation
を使って配置されます。それ故に、それはソーラーシステムを、ユーザーの現在位置の実際の緯度、経度に関係なしに、北を指して配置することを可能にします。
指示指標は太陽のAR.GeoObject
に追加されます。そのため、ユーザーは、継続して正しい指示で案内されます。
惑星のアニメーションは3D
Model sample と同様に動きます。そこでは回転の動きは、 AR.AnimationGroup
を使って結合されている複数の AR.PropertyAnimation
で作成されます。animate(planet)の機能は、動く惑星のためにそれらのアニメーションを生成するために作動します。
再度念を押すと、onClickのトリガーとされるplanetClicked()機能の3D Model sampleに類似して、HUDの上に惑星の情報は表示されます。
Combine Image Recognition and POIs(画像認識とPOIの組み合わせ)
Wikitude SDKはロケーションARベースの風景と実風景を組み合わせてユーザーにシームレスな体験を創造します。この説明はこれをどのように成し遂げるのかを示し、追加の助言をします。
仮想のお店のロゴを認識するためにAR.ImageTracker
を生成することから始めましょう。そして、それをAR.ImageTrackable
に設置しましょう。
// Create the tracker to recognize a store logo
var trackerDataSetPath = "assets/ShopLogo.wtc";
IrAndGeo.resource = new AR.TargetCollectionResource(trackerDataSetPath)
IrAndGeo.tracker = new AR.ImageTracker(IrAndGeo.resource, {
onTargetsLoaded: IrAndGeo.loadingStepDone,
onError: IrAndGeo.errorLoading
});
// Create drawables to display on the recognized image
var logo = new AR.ImageDrawable(IrAndGeo.res.logo, 1.0, {
zOrder: -1
});
// ...
IrAndGeo.menuDrawables = [logo, buttonDeal, buttonWeb, buttonStores];
IrAndGeo.dealDrawable = new AR.ImageDrawable(IrAndGeo.res.deal, 1.0, {
enabled: false,
onClick: IrAndGeo.hideDeal
});
// Create the object by defining the tracker, target name and its drawables
var imageTrackable = new AR.ImageTrackable(IrAndGeo.tracker, "ShopLogo", {
drawables: {
cam: [logo, buttonDeal, buttonWeb, buttonStores, IrAndGeo.dealDrawable, IrAndGeo.model]
},
// ...
});
お店のロゴの表面に現れた画像の最終結果
これは、認識した画像の表面にすべての引き出し可能なObjectsを表示しています。ロケーションベースのAR部分は他のすべてのARchitect Worldに類似して完成させることができます。
IrAndGeo.createMarker = function(lat, lon, name) {
var loc = new AR.GeoLocation(lat, lon);
var imageDrawable = new AR.ImageDrawable(IrAndGeo.res.marker, 2, {
scale: {
x: 0,
y: 0,
},
onClick: function() {
alert("clicked");
}
});
IrAndGeo.markerAnimations.push(new AR.PropertyAnimation(imageDrawable, 'scale.x', 0.0, 1.0, 1000, {
type: AR.CONST.EASING_CURVE_TYPE.EASE_OUT_BOUNCE
}));
IrAndGeo.markerAnimations.push(new AR.PropertyAnimation(imageDrawable, 'scale.y', 0.0, 1.0, 1000, {
type: AR.CONST.EASING_CURVE_TYPE.EASE_OUT_BOUNCE
}));
IrAndGeo.stores.push(new AR.GeoObject(loc, {
drawables: {
cam: imageDrawable
},
enabled: false
}));
};
上記の方法は受け渡された緯度と経度にマーカーを生成します。他のすべてのAR.GeoObject
と同様に、可視化された表示は多くの引き出し可能なObjectから作成されます。AR.GeoObject
はvalue enabled
set
to false
で生成されます、そのため最初は可視化されていません。画像ターゲット上のエレメントがクリックされた時、生成されたGeoObjectsがそれを可視化されたセットにします。
可視化されるお店の位置
IrAndGeo.showStores = function() {
// enable all GeoObjects
IrAndGeo.stores.forEach(function(x, idx) {
x.enabled = true;
});
// ...
};
実際の風景とロケーションベースのARを結合するはやさしく容易です。実際の風景をベースにしたARは追加のコンピューターパワー(それとバッテリーパワー)を要求することを覚えておいてください。それゆえに、それが本当に必要ならば、あなたはAR.ImageTracker
だけを生成するべきです。もしそれがもはや必要ないのならば、それをAR.ImageTracker.destroy()
.を呼び出すことで破壊してください。
サンプルを見るために、このページ on this pageの画像を使うことができます。