移動距離測定アプリの作成

移動距離測定アプリの作成

まず、headで
googleMapAPIのライブラリーをWebから読み込む

<script src="http://maps.google.com/maps/api/js?sensor=false&libraries-geometry"></script>

画面上部へ
移動開始
到着
のボタンを設置

ただし、到着ボタンは最初は非活性状態にする必要がある

button要素なら
disable属性を付けるけど

リンクボタンの場合なら
class=”ui-disabled”
を設定する
これで、見た目も機能も非活性状態にできる

ボタン2つは、
div data-role=”fieldcontain”
でまとめる

ソースは

<div data-role="fieldcontain">

<a href="#" data-role="button" data-icon="home" id="start">移動開始</a>
<a href="#" data-role="button" data-icon="check" id="goal" class="ui-disabled">到着</a>
</div>

次に、それぞれの位置情報と
移動距離はリストでまとめて表示するので
リストビューで配置する

それぞれの項目には
id=”” を付けた span 要素を使って
Javascriptで書き換え可能にする

なお、リストでは見出しを作りたいので
data-role=”list-divider”
を使う
これをつけたとこだけ色が変わるので見やすい

なおリストの角を丸くして、見栄えをよくするため
ul には
data-inset=”true”
を設定

この data-role=”” 関連の解説には
http://billboardtop100.net/2011/02/jquery-mobile-lists.html
を参考にしました

span はHTML5からできたもので
単体では意味を持たないけど
CSSの設定とかJavascript制御に使いやすいもの

詳しくは HTML5 span で検索するとわかりやすい

リストビューのソースは

<ul data-role="listview" data-inset="true">
<li data-role="list-divider">開始位置</li>
<li>緯度<span id="start-latitude"></span></li>
<li>経度:<span id="start-longitude"></span></li>
</ul>
<ul data-role="listview" data-inset="true">
<li data-role="list-divider">到着位置</li>
<li>緯度:<span id="goal-latitude"></span></li>
<li>経度:<span id="goal-longitude"></span></li>
<li>移動距離: <span id="distance"></span></li>
</ul>

そして、地図もリストビューで表示する

li要素の中へ地図を動的に作成することで
リストの中でも地図が設置できる

動的に配置するのは
移動開始ボタンが押されたら地図が表示されるようにするから

ソースは

<ul data-role="listview" data-inset="true">
<li data-role="list-divider">地図</li>
<li id="map-wrapper"></li>
</ul>

これで移動開始ボタンが押されると
id=”map-wrapper”の部分に地図が表示される

あとは、移動履歴の表示
これは、リストビューで作成して
履歴削除ボタンも付けて削除できるようにする

<ul id="history" data-role="listview" data-inset="true">
<li data-role="list-divider">移動履歴</li>
</ul>
<div data-role="fieldcontain">
<a href="#" data-role="button" data-icon="delete" id="delete-history">履歴削除</a>
</div>

GoogleMapAPIで2点間の距離を求める

GoogleMapAPIで2点間の距離を求める

2点間の距離を求めるときには
地球は平面ではなく球体なので
これを考慮する事

曲面上の距離を求める方法は
測地線航海算法が有名だけど
google map APIを使うことで
2点間の緯度経度がわかれば
簡単に距離を求めることができる

今回は、その中の
Geometry Libraryで提供されている
computeDistanceBetween()
を使う

GeometryLibraryを使うには
HTMLのヘッダでライブラリーの読み込みをする

<script src="http://maps.google.com/maps/api/js?sensor=false&libraries-geometry"></script>

そして、computeDistanceBetween()は以下のように使う

var startPosition = new google.maps.LatLng(startLatitude, startLongitude);
var endPosition = new google.maps.LatLng(endLatitude, endLongitude);

var distance = google.maps.geometry.spherical.computeDistanceBetween(startPosition,endPosition);

このように、開始位置と終了位置を
LatLngで指定して
それらを引数として
computeDistanceBetween()を呼び出す 
この関数は戻り値として
distanceを返し、これはメートル単位になる

GeoLocationAPI その2

GeoLocationAPI その2

onErrorの引数になる e は
code
message
をとる

codeには
PERMISSION_DENIED
POSITION_UNAVAILABLE
TIMEOUT
がある

端末の周りの環境によりエラーになるため
onErrorによるエラー制御は必ずしておくこと

次に、options
optionsでは、以下の設定が可能

enableHighAccuracy
高度な位置情報取得するかを
true/false で設定

timeout
タイムアウトするまでのミリ秒で指定

maximumAge
キャッシュの保持期限をミリ秒で指定

enableHighAccuracyをtrueにすると
GPSが搭載されていれば
必ずGPSを使う
逆に言えば、これを設定しないと
GPSを使わず、周りの基地局の位置情報を使うため
誤差が大きくなる

maximumAgeは
キャッシュ保持期限なので
保持期限内は同じ位置を示す
つまり移動しても反映されない
リアルタイムにするなら
maximumAgeを0にする

GeoLocation API で移動距離の計算

GeoLocation API で移動距離の計算

GeoLocationAPIは、端末の現在位置を取得するAPI

緯度経度以外に、端末によっては
高度、速度などの取得も可能

位置情報の取得は
たいていはGPSか最寄りの基地局の位置になる

位置情報は、現在位置の表示
移動距離の計算
移動中のルート表示など用途はたくさんある

GeoLocationAPIは、jQueryMobileでは非対応のため
ブラウザーが提供するJavascriptのAPIを使う

位置情報を取得するには
navigator.geolocation.getCurrentPosition()を使う

getCurrentPosition()は
位置情報取得成功時のコールバック関数のonSuccess
位置情報取得失敗時のコールバック関数のonError
位置情報オプションの
options
を引数にとる

navigator.geolocation.getCurrentPosition(onSuccess,onError,options);

となる

具体的には

navigator.geolocation.getCurrentPosition(function(pos){
alert("latitude:"+pos.coords.latitude+","+"longitude:"+pos.coords.longitude);
},function(e){
alert(e.message);
},
{"enableHighAccuracy":true});

こうすれば、成功したなら
アラートで緯度経度を表示

失敗ならエラーメッセージを表示する

getCurrentPosition()が
コールバック関数で値を取得しているのは
位置情報取得処理が非同期処理のため

このため、successのときの処理で取得した位置情報を変数へ格納して
getCurrentPosition()の前後で参照すふようなことはできない

onSuccessのときの引数のposは
位置情報を示す coords
取得した時刻を示すtimestampをもっている

また、coordsはたくさんの要素を持っている

以下はその一部

latitude
緯度

longitude
経度

altitude
高度

accuracy
精度

altitudeAccuracy
高度の精度

heading
方向

speed
速度

緯度は、南北の位置で、赤道を0°として
北は北極点の北緯90°まで
南は南極点の南緯90°まで

経度は、東西の位置で、
本初子午線を0°として
東側に向かって、東経180°まで
西側に向かって、西経180°まで

位置の取得時刻は、
postimestamp
で取得し、日付形式はUnixTimeになる

カメラアプリ起動

カメラアプリ起動

まずは標準のカメラアプリ起動から
アプリ名は MyCamera にして
Android 4.2 対応の予定

GoogleMap とは違ってAPI変更を気にしなくていいので気分は楽

編集するのは MainActivity

まず Intent のインスタンスを生成

Intent intent = new Intent();

次に
Intent.setAction() で
android.media.action.IMAGE_CAPTURE
を指定してカメラアプリを設定

intent.setAction("android.media.action.IMAGE_CAPUTRE");

今回は実験で起動するだけなので
startActivity() でカメラアプリを起動

startActivity(intent);

そして Activity を閉じるので

finish();

またカメラを使うには権限が必要になるので
AndroidManifest.xml で

<uses-permission android:name="android.permission.CAMERA"/>

が必要になる

これがないとエラーになる

現在地ボタンの設置

現在地ボタンの設置

前回、大阪駅の屋内情報を表示できたので
今回は現在地ボタンを設置

GoogleMap インスタンスを取得するけど
これは

	private GoogleMap mMap =null;

ですでに前回書いているので省略

次に
GoogleMap.setMyLocationEnabled() で
MyLocation レイヤーを有効化するので

if (mMap != null) {

の処理へ

mMap.setMyLocationEnabled(true);

を追加

次に
GoogleMap.getUiSettings() で
UiSettings インスタンスを取得するので

UiSettings settings =mMap.getUiSettings();

を追加

UiSetting.setMyLocationButtonEnabled()
で現在地ボタンを有効にする
デフォルトで有効
ということで

settings.setMyLocationButtonEnabled(true);

を追加

これでクリックすると現在地に移動するボタンができる

今回の機能追加は
現在地ボタンを表示する
を参考にしました

屋内マップの表示

屋内マップの表示

一番使えそうなので
これと現在地取得をやろう

http://seesaawiki.jp/w/moonlight_aska/d/%b2%b0%c6%e2%a4%f2%c9%bd%bc%a8/%c8%f3%c9%bd%bc%a8%a4%b9%a4%eb
を参考に行う

以前作成した usefulmap プロジェクトを改造して使う

レイアウトファイルは

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/map"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    class="com.google.android.gms.maps.MapFragment"
 />

となっている

手順としては
Google Map インスタンスの取得

private GoogleMap mMap =null;

次にマップの設定をする

デフォルトだと世界地図なので
今度いく予定の大阪のサンプルが載っているので
それを指定

private static final LatLng OSAKA_STATION = new LatLng(34.702177, 135.495114);

activity の場合は findViewById() で取得したけど
マップはフラグメントなので
findFragmentById()
を使う

これはonCreate() の中へ記述する

mMap =((SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.map)).getMap();

これで mMap で操作するための準備ができる

		if(mMap !=null){
			
		}

で地図が空でないのなら表示していく

			CameraPosition cameraPos = new CameraPosition.Builder()
			.target(OSAKA_STATION)
			.zoom(18.0f).bearing(0)
			.build();java]
でカメラを大阪駅に指定

target で位置情報の座標を指定
zoom は倍率
bering はカメラの向きで0で北
build() でインスタンス生成

mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPos));

で大阪駅まで移動する

次にGoogleMap.setIndoorenabled()

屋内マップの表示、非表示を設定
デフォルトは表示
今回は表示したいので

mMap.setIndoorEnabled(true);

途中でエラーがあった
原因は作りかけて保存したXMLファイル

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/map"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    class="com.google.android.gms.maps.SupportMapFragment"
 />


class=”” の部分が

 class="com.google.android.gms.maps.MapFragment"

となっていたのが原因

加速度センサー

加速度センサー

加速度センサーは、iOSとAndroidではブラウザー提供のJavascriptのAPIの値が変わる

ちなみに jQueryMobileでは、加速度センサーは非対応

加速度センサーの値は、
devicemotion
というブラウザーのイベントから取得する

var onDeviceMotion = function(e){
accel = e.accelerationIncludingGravity;
var accelX = accel.x;
var accelY = accel.y;
var accelZ = accel.z;
};

window.addEventListener('devicemotion',onDeviceMotion,true);

addEventListener()でイベントをハンドリングするonDeviceMotion関数を
devicemotionイベントにバインドする

onDeviceMotionでは、
受け取ったイベントeから
加速度メーターの値になる
accelerationIncludingGravity
を取得している

accelerationIncludingGravityには
x
y
z
という変数名で、各軸方向の加速度が格納されている

注意点として、devicemotionイベントは、
非常に短い間隔で発生しているため
実質的には常に最新の加速度が取得できるけど
イベントの発生頻度が高いため
イベントハンドラの中で重い処理は避けた方がよい

あと、AndroidとiOSでは加速度センサーの正負の値が逆になる

アプリがインストールされていないときの処理

アプリがインストールされていないときの処理

URLスキームは便利だけど
アプリがインストールされていないと
エラーになる

エラーはページが存在しませんというもの

このため、アプリがインストールされていないときには
アプリのダウンロードページに遷移するようにする

ちなみに
独自urlスキーム 作成 Android
で調べてみたけど
ソーシャルメディアアプリだけでなく
自作アプリでも、URLスキームの利用は可能

以下は、Twitterアプリがインストールされてないときには
ダウンロードページへ遷移する処理

<a href="twitter-btn">タイムラインの表示</a>

<iframe frameborder="1" scrolling="auto" id="twitter-launcher" name="twitter-launcher"></iframe>

でiframeの表示を overflow:hidden;で隠し
そのiframeのsrcでURLスキームリンクを指定する
location.href
ではなく
iframe のsrcなのは
必要なアプリがインストールされていないときにJavascriptで
アプリのダウンロードページへ遷移するため

また iframe の src による画面遷移の場合
親ページのJavascriptが続行して実行される

つまり、アプリがインストールされているなら iframeで処理して
アプリがインストールされていないときには
location.hrefを使い、ダウンロードページへリンクする

Javascriptで
iframe の srcで外部ドメインのサイトを指定しても
セキュリティーにより遷移できない

Androidのブラウザーのときの処理は

if(/(Android)/.test(navigator.userAgent)){
DOWNLOAD_URL=" market://search?q=pname:com.twitter.android";
}

$(document).on("pageinit","#top",function(e){
$(this).on("click","#twitter-btn",function(e){
$("#twitter-launcher").attr('src',"twitter://timeline");
setTimeout(function(){

location.href=DOWNLOAD_URL;
},100);

});
});

market://search?q=pname:com.twitter.android

AndroidのTwitterアプリのダウンロードページ
もし、同じように自作アプリでやるなら
そのアプリのダウンロードページのURLにする

$("#twitter-launcher").attr('src',"twitter://timeline");

で、iframeのsrcを追加している
これで、タイムラインを表示

setTimeout(function(){

location.href=DOWNLOAD_URL;
},100);

で、タイムアウトしたら
ダウンロードページへリンクするように設定している

URLスキームのパラメーター

URLスキームのパラメーター

URLスキームを使えば、リンクをはるだけで簡単にソーシャルメディアアプリと連携できる

ただし、そのアプリがインストールされていないとだめ

TwitterのURLスキームのパラメーターは

twitter://timeline
タイムラインの表示

twitter://mentions
@自ユーザー名を含むツイートの表示
つまり自分のユーザー名入りでツイートを表示

twitter://post?message=メッセージ
指定した文字列を初期値としてツイートダイアログをひらく
初期値なので変更可能

twitter://search?query=検索文字列
指定した文字列を含むツイートの検索

次に、facebookのURLスキームのパラメーター

fb://profile
自分のプロフィールを開く

fb://friends
自分の友達リストを開く

fb://events
イベントリストを開く

fb://albums
フォトアルバムを開く