カメラアプリで撮影

カメラアプリで撮影

まずは構文のメモ

void takePicture(Camera,ShuterCallback shutter,
Camera.PictureCallback raw,
camera.PictureCallback jpeg)
カメラの撮影をする

void takePicture(
Camera.ShutterCallback shutter,
Camera.PictureCallback raw,
Camera.PictureCallback postpreview,
Camera.PictureCallback jpeg)
カメラの撮影を行う

void onShutter()
シャッターが押されたときに呼び出すコールバック

void onPictureTaken(byte[] data,
Camera camera )
JPEGイメージ生成後に呼び出されるコールバック

それぞれの引数の意味は
shutter
シャッターが押されたときによばれるコールバックを受け取る
camera.ShutterCallbackインスタンス

raw
Rawイメージ生成時に呼ばれるコールバックを受け取る
Camera.PictureCallbackインスタンス

postview
postview のイメージデータ生成時に呼ばれるコールバックを欝ケトル
Camera.Picturecallback インスタンス

jpeg
JPEG イメージ生成自に呼ばれるコールバックを受け取る
Camera.PictureCallback インスタンス

takePicture()を呼び出すと
SurfaceView へのプレビュー表示がストップする

なので
引き続きプレビュー画面を表示するには
写真撮影処理の終わりで
startPreview() を呼び出すこと

プレビュー画面の表示には
SurfaceView を使う

これは
onCreate() で
SurfaceView を作成し
カメラのプレビュー画面を用意する

次に SurfaceView の SurfaceHolder を生成して
callback メソッドを実装する

実装した SurfaceHolder のcallbackメソッドでは
surfaceCreated() の中で
カメラを open()
プレビューディスプレイをセットする

surfaceChanged() では
カメラのパラメータでプレビューサイズをセットする

surfaceDestroyed() で
カメラプレビュー停止とリリースをしてカメラを開放する

surfaceview は
メインスレッドとは別のスレッドを描画専用で割り当てるので頻繁に画面を表示するときに使う

次に写真撮影関連

撮影するには
onCreate() の中で撮影ボタンを設置して
リスナーを登録する

次に
撮影ボタンのリスナー OnClickListener へ
takePicture() と
撮影した画像をSDカードに保存する処理 saveSD を記述する

tkePicture() では
shutter
raw
jpeg
の3つのコールバックを設定する

画像の保存はSDカードなどの外部ストレージ
ファイル名は現在時刻を使ったものにする

すでにプレビュー画面表示のアプリを作成しているのでこれを改造する

まずはシャッターが押されたときのコールバックK実装

public void surfaceDestroyed(){

}

の下へ実装する

		//シャッターが押されたときの処理
		private Camera.ShutterCallback mShutterListener = new Camera.ShutterCallback() {
			
			@Override
			public void onShutter() {
				// TODO Auto-generated method stub
				
			}
		};

を追加

そしてJPEGイメージ生成後に呼び出す処理
つまり写真をとったときの処理

//JPEG イメージ生成語の処理
		private Camera.PictureCallback mPictureListener = new Camera.PictureCallback() {
			
			@Override
			public void onPictureTaken(byte[] data, Camera camera) {
				// TODO Auto-generated method stub
				//sd card jpeg データを保存
				if(data != null){
					FileOutputStream myFOS=null;
					try {
						myFOS = new FileOutputStream("/sdcard/camera_test.jpeg");
						myFOS.write(data);
						myFOS.close();
					} catch (Exception e) {
						// TODO: handle exception
						e.printStackTrace();
					}
					//preview start
					camera.startPreview();
				}
			}
		};

となる

そして最後に写真をとる処理
これは
onTouchEvent() で実装


		@Override
		public boolean onTouchEvent(MotionEvent event) {
			// TODO Auto-generated method stub
			//camera preview end
			if(event.getAction() == MotionEvent.ACTION_DOWN){
				if(mCamera !=null){
					//写真撮影
					mCamera.takePicture(mShutterListener, null, mPictureListener);
					return true;
				}
			}
			return super.onTouchEvent(event);
		}

あと、外部ストレージ保存するので

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

が必要になる

とりあえずできたけど
nexus7 外部ストレージがなかった
あと、カメラの向きおかしいので修正の必要があるので
今後修正

次はオートフォーカスにチャレンジ予定

jekyII

jekyII

読み方はジキル

これは静的サイト作成ジェネレータ

記法として markdown が使える

ブログなどでも活用できる

公式サイトは
http://jekyllrb.com/

jekyll は ruby の gem で提供されている

これを使うための知識は
html
css
ruby
そして markdown

開発環境は vagrant

作業用ディレクトリとして

mkdir jekyll_lessons 

を作成

ruby ver 2.1.1
gem ver 2.2.2
IP は ifconfig で確認する

インストールは

gem install jekyll

でOK

時間がかかるので余裕のあるときに行うようにすること

あと、ドットインストールでは
Markdown には
sublime text を使っているけど
私の環境は Ubuntu

そして今までほとんど vim オンリーだったので
Ubuntu markdown で検索した結果

ReText MarkDownに対応 PDF, ODT, HTMLにエクスポートできるテキストエディタ

を発見

この記事での ver は Ubuntu 10.04 LTS だけど
今は Ubuntu 12.04 LTS なので
Ubuntu ソフトウェアセンターを起動し

ReText で調べればすぐにヒット
あとはインストールするだけでOKでした

Jekyll でつまづいたら
先に markdown をやろうと思います

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

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

まず、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では加速度センサーの正負の値が逆になる