アンドロイドでAR(拡張現実)
工事中
コードを若干変更。
初期画面はこんな感じ。現行のコードでは、Mapsを一度アクティブにする必要があるみたい(この辺はもう少し調べてみます)。
初期化画面を変更しました(splash)。
この初期画面が表示されると、即カメラ画面へ遷移します。
New
レイアウトを変更しました。
:ドラッグで移動 |
---|
:JavaScriptから制御、初期画面では見えませんが、位置を与えることで表示 |
---|
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.wisteria.arguide2" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="10" /> <supports-screens android:largeScreens="true" android:normalScreens="true" android:smallScreens="true" android:resizeable="true" android:anyDensity="true" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.Camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-feature android:name="android.hardware.camera.flash" /> <uses-feature android:name="android.hardware.Camera.Parameters" /> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <application android:icon="@drawable/cat" android:label="@string/app_name" > <activity android:name=".AR_GuideIIActivity" android:label="@string/app_name" android:configChanges="orientation|keyboardHidden" android:screenOrientation="landscape" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
package com.wisteria.arguide2; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.Intent; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; //GPS import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.location.LocationProvider; //compass sensor import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; //TextToSpeech import android.speech.tts.TextToSpeech; import android.speech.tts.TextToSpeech.OnInitListener; //SpeechRecognizer import android.speech.RecognizerIntent; import java.util.ArrayList; import android.os.Bundle; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.webkit.WebChromeClient; import android.webkit.WebView; import android.content.Context; import android.os.Handler; import android.widget.TextView; import android.widget.EditText; import android.widget.TextView.BufferType; //import android.widget.Button; import android.widget.ImageButton; import android.widget.LinearLayout; import android.view.View.OnClickListener; import android.view.Display; import android.util.DisplayMetrics; import android.app.AlertDialog; import com.phonegap.*; //info import android.provider.Settings; import android.telephony.*; public class AR_GuideIIActivity extends DroidGap implements OnInitListener, LocationListener { /** Called when the activity is first created. */ private WebView webView = null; private float marker_x = -1; private float marker_y = -1; private float cat_x = -1; private float cat_y = -1; private CameraPreview cp; private int screen_width = -1; private int screen_height = -1; private float x_dpi = -1; private float y_dpi = -1; private int m_counter = 0; private Bitmap bitmap_A; private Bitmap bitmap_B; // private AlertDialog.Builder dlg; //TextToSpeech private TextToSpeech tts; // private EditText textedit; private TextView tv1; private TextView tv2; private Handler mHandler; private String result_content; // private float horizontal_angle; // private String c_lat; private String c_lng; private String c_elev; private String north_direction; private double prev_direction; private int prev_dir = -1; private double direction; private String pitch_rotation; private String roll_ratation; //softKeyBoard private boolean skb_flag = false; //compass sensor private SensorManager mSensorManager; private Sensor mSensor; private final SensorEventListener mListener = new SensorEventListener() { public void onSensorChanged(SensorEvent event) { if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) { String sensor_changed = event.values[0] + "\n" + event.values[1] + "\n" + event.values[2]; tv2.setText(sensor_changed,BufferType.NORMAL); direction = event.values[0] * 0.1 + prev_direction * 0.9; north_direction = direction + ""; pitch_rotation = event.values[1] + ""; roll_ratation = event.values[2] + ""; prev_direction = event.values[0]; //指定時の直前・直後の20個くらいの値の中央値を見る //あるいは、5度ごとに変化を見る(描画頻度の軽減) int dir = (int)event.values[0]; /* Lite *********************** ****************** */ int dir1 = dir/10; dir1 = dir1 * 10; int dif = dir - dir1; if ((dif >= 0)&&(dif <= 3)) { dir1 = dir1 + 0; }else if ((dif > 3)&&(dif <= 7)) { dir1 = dir1 + 5; }else{ dir1 = dir1 + 10; } if (prev_dir != -1) { //softKeyBoard if ((dir1 != prev_dir)&&(skb_flag == false)) { webView.loadUrl("javascript:get_val(" + dir1 + ")"); } } prev_dir = dir1; // } } public void onAccuracyChanged(Sensor sensor, int accuracy) { // } }; //GPS private LocationManager mLocationManager; // private static final int REQUEST_CODE = 0; // @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // dlg = new AlertDialog.Builder(this); //sensor mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE); mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); //GPS mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE); // mHandler = new Handler(); // getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); requestWindowFeature(Window.FEATURE_NO_TITLE); cp = new CameraPreview(this); setContentView(cp); // addContentView(new OverlayView(this), new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); // WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE); Display disp = wm.getDefaultDisplay(); DisplayMetrics Metrics = new DisplayMetrics(); disp.getMetrics(Metrics); screen_width = disp.getWidth(); screen_height = disp.getHeight(); x_dpi = Metrics.xdpi; y_dpi = Metrics.ydpi; marker_x = disp.getWidth() / 2 - 50; marker_y = disp.getHeight() / 2 - 50; LinearLayout linear_LayoutL = new LinearLayout(this); linear_LayoutL.setOrientation(LinearLayout.VERTICAL); linear_LayoutL.setGravity(Gravity.LEFT|Gravity.CENTER); addContentView(linear_LayoutL, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); LinearLayout linear_LayoutR = new LinearLayout(this); linear_LayoutR.setOrientation(LinearLayout.VERTICAL); linear_LayoutR.setGravity(Gravity.RIGHT|Gravity.CENTER); addContentView(linear_LayoutR, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); LinearLayout linear_LayoutC = new LinearLayout(this); linear_LayoutC.setOrientation(LinearLayout.VERTICAL); linear_LayoutC.setGravity(Gravity.CENTER); addContentView(linear_LayoutC, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); ImageButton imgbtn_A = new ImageButton(this); imgbtn_A.setImageResource(R.drawable.mapicon); imgbtn_A.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { imagButtonA_OnClick(); } }); linear_LayoutR.addView(imgbtn_A, new LinearLayout.LayoutParams(100,100)); ImageButton imgbtn_B = new ImageButton(this); imgbtn_B.setImageResource(R.drawable.speaker); imgbtn_B.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { imagButtonB_OnClick(); } }); linear_LayoutR.addView(imgbtn_B, new LinearLayout.LayoutParams(100,100)); ImageButton imgbtn_C = new ImageButton(this); imgbtn_C.setImageResource(R.drawable.gmaps); imgbtn_C.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { imagButtonC_OnClick(); } }); linear_LayoutR.addView(imgbtn_C, new LinearLayout.LayoutParams(100,100)); ImageButton imgbtn_D = new ImageButton(this); imgbtn_D.setImageResource(R.drawable.sr); imgbtn_D.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { imagButtonD_OnClick(); } }); linear_LayoutR.addView(imgbtn_D, new LinearLayout.LayoutParams(100,130)); textedit = new EditText(this); textedit.setText("info",BufferType.NORMAL); textedit.setHeight(50); textedit.setEnabled(false); textedit.setFocusableInTouchMode(true); linear_LayoutL.addView(textedit, new LinearLayout.LayoutParams(170,250)); tv1 = new TextView(this); tv1.setText("GPS information"); linear_LayoutL.addView(tv1, new LinearLayout.LayoutParams(170,70)); tv2 = new TextView(this); tv2.setText("Compass information"); linear_LayoutL.addView(tv2, new LinearLayout.LayoutParams(170,80)); // webView = new WebView(this); webView.setWebChromeClient(new WebChromeClient()); webView.getSettings().setJavaScriptEnabled(true); JavascriptAdapter jsobj = new JavascriptAdapter(this); webView.addJavascriptInterface(jsobj, "android_ar"); webView.loadUrl("file:///android_asset/www/index.html"); webView.setVisibility(View.GONE); linear_LayoutC.addView(webView, new LinearLayout.LayoutParams(disp.getWidth()* 1,disp.getHeight()* 1)); //TextToSpeech tts = new TextToSpeech(getApplicationContext(), this); // show_map(); // } private void imagButtonA_OnClick(){ webView.loadUrl("javascript:loc_judgement()"); } private void imagButtonB_OnClick(){ webView.loadUrl("javascript:speak_it()"); } private void imagButtonC_OnClick(){ show_map(); } private void show_map(){ m_counter++; if (c_lat == null) { c_lat = "0"; } if (c_lng == null) { c_lng = "0"; } String arg = c_lat + "," + c_lng + "," + screen_width + "," + screen_height + "," + m_counter; webView.loadUrl("javascript:get_mapinfo(" + arg + ")"); webView.setVisibility(View.VISIBLE); } private void set_info(){ //android_id String deviceId = Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID); //IMEI TelephonyManager mTelephonyMgr =(TelephonyManager)getSystemService(TELEPHONY_SERVICE); String subscriberId = mTelephonyMgr.getSubscriberId(); webView.loadUrl("javascript:get_myid('" + deviceId + "/" + subscriberId + "')"); } private void imagButtonD_OnClick(){ //SpeechRecognizer try { Intent intent = new Intent( RecognizerIntent.ACTION_RECOGNIZE_SPEECH); intent.putExtra( RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); intent.putExtra( RecognizerIntent.EXTRA_PROMPT, "SpeechRecognizer"); // startActivityForResult(intent, REQUEST_CODE); } catch (ActivityNotFoundException e) { String resultsString = "ActivityNotFoundException"; webView.loadUrl("javascript:get_speechinfo('" + resultsString + "')"); } } //SpeechRecognizer @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) { String results = ""; ArrayList<String> strings = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS); if (strings.size() == 0) { results = "No Data"; }else{ int len; if (strings.size() > 4) { len = 4; }else{ len = strings.size(); } for (int i = 0; i< len; i++) { results += strings.get(i) + "/"; } show_map(); } webView.loadUrl("javascript:get_speechinfo('" + results + "')"); } super.onActivityResult(requestCode, resultCode, data); } class JavascriptAdapter { private Context con; public JavascriptAdapter(Context con) { this.con = con; } // public void start_tts(String speec_content){ tts.speak(speec_content, TextToSpeech.QUEUE_FLUSH, null); } // public void seImageOnscreen(float mx,float my,float cx,float cy) { OverlayView ol = new OverlayView(con); ol.set_imagePos(mx,my,cx,cy); } public void open_softkeyboard(){ //softKeyBoard skb_flag = true; // (new Thread(new Runnable() { @Override public void run() { mHandler.post(new Runnable() { @Override public void run() { webView.requestFocus(View.FOCUS_DOWN); } }); } })).start(); } public void hide_map() { //softKeyBoard if (skb_flag == true) { skb_flag = false; } // set_info(); // (new Thread(new Runnable() { @Override public void run() { mHandler.post(new Runnable() { @Override public void run() { webView.setVisibility(View.GONE); } }); } })).start(); } public String ret_information() { //OverlayView ol = new OverlayView(con); //String ret = ol.get_imagePos(); float focal_length = cp.get_camerainfo(); if (c_lat == null) { c_lat = ""; } if (c_lng == null) { c_lng = ""; } //portrate //String ret = c_lat + "/" + c_lng + "/" + north_direction + "/" + pitch_rotation + "/" + roll_ratation + "/" + focal_length + "/" + screen_height + "/" + y_dpi; //landscape String ret = c_lat + "/" + c_lng + "/" + north_direction + "/" + pitch_rotation + "/" + roll_ratation + "/" + focal_length + "/" + screen_width + "/" + x_dpi; return ret; } public void set_result(String result) { result_content = result; (new Thread(new Runnable() { @Override public void run() { mHandler.post(new Runnable() { @Override public void run() { textedit.setText(result_content,BufferType.NORMAL); } }); } })).start(); } } class OverlayView extends View { public OverlayView(Context context) { super(context); bitmap_A = BitmapFactory.decodeResource(context.getResources(), R.drawable.androidmarker); bitmap_B = BitmapFactory.decodeResource(context.getResources(), R.drawable.maneki); setFocusable(true); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.TRANSPARENT); if (marker_x >= 0) { canvas.drawBitmap(bitmap_A, marker_x, marker_y, null); } if (cat_x >= 0) { canvas.drawBitmap(bitmap_B, cat_x, cat_y, null); } } @Override public boolean onTouchEvent(MotionEvent event) { marker_x = event.getX() - 50; marker_y = event.getY() - 50; invalidate(); return true; } public void set_imagePos(float mx,float my,float cx,float cy){ if ((mx != -1)&&(my != -1)){ marker_x = mx; marker_y = my; } if ((cx != -1)&&(cy != -1)){ cat_x = cx; cat_y = cy; } invalidate(); } } @Override protected void onResume(){ //GPS if (mLocationManager != null) { mLocationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 0, 0, this); } super.onResume(); //compass sensor mSensorManager.registerListener(mListener, mSensor, SensorManager.SENSOR_DELAY_GAME); } @Override protected void onStop(){ //GPS mSensorManager.unregisterListener(mListener); super.onStop(); } @Override protected void onPause() { //GPS if (mLocationManager != null) { mLocationManager.removeUpdates(this); } super.onPause(); } @Override public void onLocationChanged(Location location) { c_lat = String.valueOf(location.getLatitude()); c_lng = String.valueOf(location.getLongitude()); c_elev = String.valueOf(location.getAltitude()); String sensor_changed = String.valueOf(location.getLatitude()) + "\n" + String.valueOf(location.getLongitude()) + "\n" + String.valueOf(location.getAltitude()); tv1.setText(sensor_changed,BufferType.NORMAL); String arg = c_lat + "," + c_lng; webView.loadUrl("javascript:move_map(" + arg + ")"); //GPS } @Override public void onProviderDisabled(String provider) { //GPS } @Override public void onProviderEnabled(String provider) { //GPS } @Override public void onStatusChanged(String provider, int status, Bundle extras) { //GPS switch (status) { case LocationProvider.AVAILABLE: // break; case LocationProvider.OUT_OF_SERVICE: // break; case LocationProvider.TEMPORARILY_UNAVAILABLE: // break; } } @Override public void onInit(int status) { //TextToSpeech if (status == TextToSpeech.SUCCESS) { // } else { // } } }
package com.wisteria.arguide2; import java.io.IOException; import java.util.List; import android.app.Activity; import android.content.Context; import android.hardware.Camera; import android.hardware.Camera.ErrorCallback; import android.hardware.Camera.PreviewCallback; import android.os.Handler; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.widget.Toast; import android.widget.TextView.BufferType; class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { protected Context context; private SurfaceHolder holder; protected Camera camera; CameraPreview(Context context) { super(context); this.context = context; holder = getHolder(); holder.addCallback(this); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } @Override public void surfaceCreated(SurfaceHolder holder) { Log.d("TEST", "surfaceCreated"); if (camera == null) { try { camera = Camera.open(); } catch (RuntimeException e) { ((Activity)context).finish(); Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show(); } } if (camera != null) { camera.setPreviewCallback(new PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) { Log.d("TEST", "onPreviewFrame: preview: data=" + data); } }); camera.setOneShotPreviewCallback(new PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) { Log.d("TEST", "onPreviewFrame: short preview: data=" + data); } }); camera.setErrorCallback(new ErrorCallback() { @Override public void onError(int error, Camera camera) { Log.d("TEST", "onError: error=" + error); } }); } try { camera.setPreviewDisplay(holder); } catch (IOException e) { camera.release(); camera = null; ((Activity)context).finish(); Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.d("TEST", "surfaceChanged"); if (camera == null) { ((Activity)context).finish(); } else { camera.stopPreview(); setPictureFormat(format); setPreviewSize(width, height); camera.startPreview(); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.d("TEST", "surfaceDestroyed"); if (camera != null) { camera.stopPreview(); camera.release(); camera = null; } } protected void setPictureFormat(int format) { try { Camera.Parameters params = camera.getParameters(); List<Integer> supported = params.getSupportedPictureFormats(); if (supported != null) { for (int f : supported) { if (f == format) { params.setPreviewFormat(format); camera.setParameters(params); break; } } } } catch (Exception e) { e.printStackTrace(); } } protected void setPreviewSize(int width, int height) { Camera.Parameters params = camera.getParameters(); List<Camera.Size> supported = params.getSupportedPreviewSizes(); if (supported != null) { for (Camera.Size size : supported) { if (size.width <= width && size.height <= height) { params.setPreviewSize(size.width, size.height); camera.setParameters(params); break; } } } } protected void setAntibanding(String antibanding) { Camera.Parameters params = camera.getParameters(); List<String> supported = params.getSupportedAntibanding(); if (supported != null) { for (String ab : supported) { if (ab.equals(antibanding)) { params.setAntibanding(antibanding); camera.setParameters(params); break; } } } } protected void setColorEffect(String effect) { Camera.Parameters params = camera.getParameters(); List<String> supported = params.getSupportedColorEffects(); if (supported != null) { for (String e : supported) { if (e.equals(effect)) { params.setColorEffect(effect); camera.setParameters(params); break; } } } } protected void setFlashMode(String flash_mode) { Camera.Parameters params = camera.getParameters(); List<String> supported = params.getSupportedFlashModes(); if (supported != null) { for (String fm : supported) { if (fm.equals(flash_mode)) { params.setFlashMode(flash_mode); camera.setParameters(params); break; } } } } protected void setFocusMode(String focus_mode) { Camera.Parameters params = camera.getParameters(); List<String> supported = params.getSupportedFocusModes(); if (supported != null) { for (String fm : supported) { if (fm.equals(focus_mode)) { params.setFocusMode(focus_mode); camera.setParameters(params); break; } } } } protected void setSceneMode(String scene_mode) { Camera.Parameters params = camera.getParameters(); List<String> supported = params.getSupportedSceneModes(); if (supported != null) { for (String sm : supported) { if (sm.equals(scene_mode)) { params.setSceneMode(scene_mode); camera.setParameters(params); break; } } } } protected void setWhiteBalance(String white_balance) { Camera.Parameters params = camera.getParameters(); List<String> supported = params.getSupportedWhiteBalance(); if (supported != null) { for (String wb : supported) { if (wb.equals(white_balance)) { params.setWhiteBalance(white_balance); camera.setParameters(params); break; } } } } public float get_cameraInfo() { //画角取得 Camera.Parameters param = camera.getParameters(); float horizontal_angle = param.getHorizontalViewAngle(); float vertical_angle = param.getVerticalViewAngle(); return horizontal_angle; }
package com.wisteria.arguide2; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; public class Overlay extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(new CameraPreview(this)); addContentView(new OverlayView(this), new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); } class OverlayView extends View { private float x = -1; private float y = -1; private Bitmap bitmap; public OverlayView(Context context) { super(context); bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.androidmarker); setFocusable(true); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.TRANSPARENT); if (x >= 0) { canvas.drawBitmap(bitmap, x, y, null); } } @Override public boolean onTouchEvent(MotionEvent event) { x = event.getX(); y = event.getY(); invalidate(); return true; } } }
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title></title> <meta name="viewport" content="width=device-width, initial-scale=1,minimum-scale=1, maximum-scale=1, user-scalable=no"> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css" /> <script src="http://code.jquery.com/jquery-1.6.1.min.js"></script> <script src="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js"></script> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css" /> <link href="http://www.google.com/uds/css/gsearch.css" type="text/css" rel="stylesheet"/> <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true&language=ja"></script> <script type="text/javascript" charset="utf-8" src="script.js"></script> <script type="text/javascript" charset="utf-8"> var information = "";// function ini(){ map_initialize(); //android_ar.seImageOnscreen(100,100,100,100); } function onSuccess_ajax(data, status){ information = data; android_ar.set_result(data); } function onError_ajax(data, status){ // handle an error alert("err"); } function ajax_job(infos){ infos_list = infos.split("/"); var data1 = infos_list[0]; var data2 = infos_list[1]; var data3 = infos_list[2]; var data4 = infos_list[3]; var data5 = infos_list[4]; data1 = encodeURI(data1); data2 = encodeURI(data2); data3 = encodeURI(data3); data4 = encodeURI(data4); data5 = encodeURI(data5); var formData = "data1=" + data1 + "&data2=" + data2 + "&data3=" + data3 + "&data4=" + data4 + "&data5=" + data5; //var url_ajax = "http://wisteriahill.sakura.ne.jp/jqmajax/call_mapdb.php"; var url_ajax = "http://wisteriahill.sakura.ne.jp/jqmajax/call_wisteriahill.php"; $.ajax({ type: "POST", url: url_ajax, cache: false, data: formData, success: onSuccess_ajax, error: onError_ajax }); return false; } function loc_judgement(){ var infos = android_ar.ret_information(); infos_list = infos.split("/"); var focal_length = infos_list[5]; var screen_width = infos_list[6]; screen_width = screen_width; var x_dpi = infos_list[7]; if((infos_list[0] == "0")&&(infos_list[1] == "0")){ alert("GPSデータがありません"); return; } ajax_job(infos); //GeoLocationで位置を取得し、データベースから該当する情報を取得する //データベースはGoogleやYahooのWebサービスでも可 } function speak_it(){ if (information == "") { information = "とりたててお話するようなことは、なにもありません。"; } android_ar.start_tts(information); } function hide_map(){ android_ar.hide_map(); } </script> </head> <body onLoad="ini()"> <div id="map_canvas" style="top:30px;left:0px;width:100%;height:250px;position:absolute;visibility:hidden" Google Maps </div> <div id="map_operation" style="top:30px;left:430px;position:absolute;visibility:hidden"> <br> <input type="button" style="width:60px;position:absolute" value="戻る" onClick="hide_map()"> <br> <input type="button" style="width:60px;position:absolute" value="情報" onClick="show_info()"> <br> <input type="button" style="width:60px;position:absolute" value="Button C"> <br> <input type="text" style="width:30px" id="mon" value=""> </div> <div id="map_append" style="top:30px;left:0px;width:50%;height:250px;position:absolute;visibility:visible;overflow:auto"> Welcome To WisteriaHill's AR </body> </html>
var map; var m_counter = 0; var map_latlng; var map_width; var polyline = null; var path = []; function dom_initialize() { // } function map_initialize() { //デフォルトの位置 map_latlng = new google.maps.LatLng(35.36286726458948,138.73157501220703); var map_Options = { zoom: 12, center: map_latlng, mapTypeId: google.maps.MapTypeId.ROADMAP, navigationControl:true, draggable:true }; map = new google.maps.Map(document.getElementById("map_canvas"), map_Options); var pageHeight = $(document).height(); $("#map_canvas").css("height",pageHeight); google.maps.event.addListener(map, 'tilesloaded', function(event) { // if (m_counter == 0) { hide_map(); } }); marker = new google.maps.Marker({ map: map, icon:"img/phone.png", raiseOnDrag:true,//bouncy position: map_latlng, animation: google.maps.Animation.DROP, clickable:true, draggable:false }); } function HomeControl(controlDiv, map) { var controlUI = document.createElement('DIV'); controlUI.style.backgroundColor = 'white'; controlUI.title = 'direction'; controlDiv.appendChild(controlUI); var controlText = document.createElement('DIV'); controlUI.id = 'test_button'; controlText.style.top = '80px'; controlText.style.left = '100px'; controlText.innerHTML = '<input type="button" value="戻る" onClick="hide_map()">'; controlUI.appendChild(controlText); } function get_mapinfo(lat,lng,width,height,counter){ m_counter = counter; map_width = width; document.getElementById("map_canvas").style.width = width*1 + "px"; document.getElementById("map_canvas").style.height = height*0.6 + "px"; if (lat == 0){ lat = 35.36286726458948; } if (lng == 0){ lng = 138.73157501220703; } map_latlng = new google.maps.LatLng(lat,lng); map.setCenter(map_latlng); if (m_counter != 0) { document.getElementById("map_canvas").style.visibility = "visible"; document.getElementById("map_operation").style.visibility = "visible"; document.getElementById("map_append").style.visibility = "hidden"; } marker.setPosition(map_latlng); } function move_map(lat,lng){ map_latlng = new google.maps.LatLng(lat,lng); map.setCenter(map_latlng); marker.setPosition(map_latlng); } function get_speechinfo(speechresult){ var temp_list = speechresult.split("/"); var html_content = ""; html_content += '<input type="text" id="selected_item" value=""><br>'; if (temp_list.length != 0) {if (temp_list.length > 4) { var max = 4; }else{ var max = temp_list.length; }for (var i = 0;i < max;i++) { if (temp_list[i] != "") { html_content += '<table><tr>'; html_content += '<th><input type="radio" name="sel_speech" id="sel_speech' + i + '" value="' + i + '"></th><th><font size=5 id="item_font' + i + '" onClick="sel_item(' + i + ')">' + temp_list[i] + '</font></th>'; html_content += '</tr></table><br>'; } } document.getElementById("map_canvas").style.visibility = "hidden"; document.getElementById("map_operation").style.visibility = "visible"; document.getElementById("map_append").style.visibility = "visible"; document.getElementById("map_append").innerHTML = html_content; open_softkeyboard(); } // } function open_softkeyboard(){ android_ar.open_softkeyboard(); } function sel_item(index){ var id = "sel_speech" + index; document.getElementById(id).checked = true; var id2 = "item_font" + index; var item_name = document.getElementById(id2).innerHTML; document.getElementById("selected_item").value = item_name; } function get_val(val){ easy_direction(val); document.getElementById("mon").value = val; } function easy_direction(angle){ var pix_num = 200;//スクリーン上のピクセル数 // angle += 90; var map_bounds = map.getBounds(); var swLatlng = map_bounds.getSouthWest(); var neLatlng = map_bounds.getNorthEast(); var sw_lng = swLatlng.lng(); var ne_lng = neLatlng.lng(); var dif = Math.abs(sw_lng - ne_lng); var degparpix = Math.abs(dif / map_width); var deg = pix_num * degparpix; if (((angle >= 0)&&(angle <= 90))||((angle > 360)&&(angle <= 450))){ var rad = angle * Math.PI / 180; var new_lng = deg * Math.sin(rad) + map.getCenter().lng(); var new_lat = deg * Math.cos(rad) + map.getCenter().lat(); }else if ((angle > 90)&&(angle <= 180)){ angle = 180 - angle; var rad = angle * Math.PI / 180; var new_lng = deg * Math.sin(rad) + map.getCenter().lng(); var new_lat = map.getCenter().lat() - deg * Math.cos(rad); }else if ((angle > 180)&&(angle <= 270)){ angle = angle - 180; var rad = angle * Math.PI / 180; var new_lng = map.getCenter().lng() - deg * Math.sin(rad); var new_lat = map.getCenter().lat() - deg * Math.cos(rad); }else if ((angle > 270)&&(angle <= 360)){ angle = 360 - angle; var rad = angle * Math.PI / 180; var new_lng = map.getCenter().lng() - deg * Math.sin(rad); var new_lat = deg * Math.cos(rad) + map.getCenter().lat(); } if (polyline != null){ polyline.setMap(null); path = [] } path.push(map.getCenter()); path.push(new google.maps.LatLng(new_lat,new_lng)); var c="#00f"; var sw=5; var so=1.0; polyline = new google.maps.Polyline({map: map, path: path,strokeColor: c, strokeOpacity:so, strokeWeight:sw}); polyline.setMap(map); } function move_cat(){ //dummy //marker_x,marker_y,cat_x,cat_y android_ar.seImageOnscreen(-1,-1,200,300); }
<?php $data1 = $_POST['data1']; $data2 = $_POST['data2']; $data3 = $_POST['data3']; $data4 = $_POST['data4']; $data5 = $_POST['data5']; $data1 = urldecode($data1); $data2 = urldecode($data2); $data3 = urldecode($data3); $data4 = urldecode($data4); $data5 = urldecode($data5); $north_direction = $data3; $camera_direction = $north_direction + 90; $l_angle = 360 / 16; $l_deg1 = 0; $l_deg2 = $l_angle * 1; $l_deg3 = $l_angle * 3; $l_deg4 = $l_angle * 5; $l_deg5 = $l_angle * 7; $l_deg6 = $l_angle * 9; $l_deg7 = $l_angle * 11; $l_deg8 = $l_angle * 13; $l_deg9 = $l_angle * 15; $l_deg10 = $l_angle * 16; $l_deg11 = $l_angle * 17; $l_deg12 = $l_angle * 19; $l_deg13 = $l_angle * 20; //大雑把に if ((($camera_direction >= $l_deg1)&&($camera_direction < $l_deg2))||(($camera_direction >= $l_deg9)&&($camera_direction <= $l_deg10))) { $res = "カメラの方向は、だいたい北です"; } else if (($camera_direction >= $l_deg2)&&($camera_direction < $l_deg3)) { $res = "カメラの方向は、だいたい北東です"; } else if (($camera_direction >= $l_deg3)&&($camera_direction < $l_deg4)) { $res = "カメラの方向は、だいたい東です"; } else if (($camera_direction >= $l_deg4)&&($camera_direction < $l_deg5)) { $res = "カメラの方向は、だいたい南東です"; } else if (($camera_direction >= $l_deg5)&&($camera_direction < $l_deg6)) { $res = "カメラの方向は、だいたい南です"; } else if (($camera_direction >= $l_deg6)&&($camera_direction < $l_deg7)) { $res = "カメラの方向は、だいたい南西です"; } else if (($camera_direction >= $l_deg7)&&($camera_direction < $l_deg8)) { $res = "カメラの方向は、だいたい西です"; } else if (($camera_direction >= $l_deg8)&&($camera_direction < $l_deg9)) { $res = "カメラの方向は、だいたい北西です"; } //カメラ用に追加(大雑把) if (($camera_direction >= $l_deg10)&&($camera_direction < $l_deg11)) { $res = "カメラの方向は、だいたい北です"; } else if (($camera_direction >= $l_deg11)&&($camera_direction < $l_deg12)) { $res = "カメラの方向は、だいたい北東です"; } else if (($camera_direction >= $l_deg12)&&($camera_direction <= $l_deg13)) { $res = "カメラの方向は、だいたい東です"; } //もうちっと細かく $m_angle = 360 / 32; $m_deg1 = 0; $m_deg2 = $m_angle * 1; $m_deg3 = $m_angle * 7; $m_deg4 = $m_angle * 9; $m_deg5 = $m_angle * 15; $m_deg6 = $m_angle * 17; $m_deg7 = $m_angle * 23; $m_deg8 = $m_angle * 25; $m_deg9 = $m_angle * 31; $m_deg10 = $m_angle * 32; $m_deg11 = $m_angle * 33; $m_deg12 = $m_angle * 39; $m_deg13 = $m_angle * 40; if ((($camera_direction >= $m_deg1)&&($camera_direction < $m_deg2))||(($camera_direction >= $m_deg9)&&($camera_direction <= $m_deg10))) { $res = "カメラの方向は、ほぼ北です"; } else if (($camera_direction >= $m_deg3)&&($camera_direction <= $m_deg4)) { $res = "カメラの方向は、ほぼ東です"; } else if (($camera_direction >= $m_deg5)&&($camera_direction <= $m_deg6)) { $res = "カメラの方向は、ほぼ南です"; } else if (($camera_direction >= $m_deg7)&&($camera_direction <= $m_deg8)) { $res = "カメラの方向は、ほぼ西です"; } //カメラ用に追加(ちと細かく) if (($camera_direction >= $m_deg10)&&($camera_direction <= $m_deg11)) { $res = "カメラの方向は、ほぼ北です"; } else if (($camera_direction >= $m_deg12)&&($camera_direction <= $m_deg13)) { $res = "カメラの方向は、ほぼ東です"; } print($res); ?>
地図上の地物がカメラに映りこんでいる時、その物体はカメラの中でどの位の角度を持つか?...ていうのをやってみました。
ここでは、”(地物)物体”とは、ComputerVisionなどでいう物体認識ことではなく、「座標」のことです(^^)。
こんな感じ。アバウトですが、検証してみるとそこそこいけました。
水平方向に投影するとアイコンが「この辺!」と示してくれるというすんぽうです。
もちろん180以上の場合は、カメラは反対方向を向いているので注意です。
関数はこんな感じ。
north_directionはスマホのcompassなんかで取得するheading角(スマホのてっぺんが北に対して何度なのかというやつ)。
で、θ=90 なら、カメラはその物体の方を向いてます。
function calc_angle(north_direction){ //north virtual point var x1 = c_lng; var y1 = parseFloat(c_lat) + 1; //my point var x2 = c_lng; var y2 = c_lat; //target point var x3 = target_lng; var y3 = target_lat; var ax = x3 - x2; var ay = y3 - y2; var bx = x1 - x2; var by = y1 - y2; var theta_rad = Math.acos(((ax * bx) + (ay * by))/(Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))); var theta_deg = Math.round(theta_rad * (180 / Math.PI)); if (c_lng >= target_lng) { theta_deg = 360 - theta_deg; } if (theta_deg < north_direction) { var θ = 360 - north_direction + theta_deg; } else { var θ = theta_deg - north_direction; } return θ; }
「カメラの中での角度」の別のロジックです。
こんな感じ。
JavaScriptです。
【script.js】
function calc_angle_II(){ if (info_flag == false) { return; } if ((vc_lng == 0)&&(vc_lat == 0)){ alert("heading角が未取得"); return; } //virtual camera view point var x1 = vc_lng; var y1 = vc_lat; //my point var x2 = c_lng; var y2 = c_lat; //target point var x3 = target_lng; var y3 = target_lat; var ax = x3 - x2; var ay = y3 - y2; var bx = x1 - x2; var by = y1 - y2; var theta_rad = Math.acos(((ax * bx) + (ay * by))/(Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))); var theta_deg = Math.round(theta_rad * (180 / Math.PI)); var LR = judge_LR(theta_deg); if (LR == "") { return; } LR_list = LR.split("/"); var dir_A = LR_list[0]; var dir_B = LR_list[1]; var dir_C = ""; if (dir_A == "L") { dir_C = "左"; }else if (dir_A == "R") { dir_C = "右"; } if (dir_B == "forward") { dir_C += "前方"; }else if (dir_B == "behind") { dir_C += "後方"; } if ((theta_deg >= 0)&&(theta_deg < 10)) { dir = "ほぼこの方向"; } else if ((theta_deg >= 10)&&(theta_deg < 40)) { dir = "やや" + dir_C; } else if ((theta_deg >= 40)&&(theta_deg < 70)) { dir = "だいぶ" + dir_C; } else if ((theta_deg >= 70)&&(theta_deg < 90)) { dir = "ずーと" + dir_C; } else if ((theta_deg >= 90)&&(theta_deg < 110)) { dir = "やや" + dir_C; } else if ((theta_deg >= 110)&&(theta_deg < 140)) { dir = "だいぶ" + dir_C; } else if ((theta_deg >= 140)&&(theta_deg < 180)) { dir = "ずーと" + dir_C; } var t_distance = Mapdistance(c_lng,c_lat,target_lng,target_lat); t_distance = Math.round(t_distance); var hosoku = calc_time(t_distance); var unit = "m"; if (t_distance >= 1000) { t_distance = t_distance / 1000; t_distance = Math.round(t_distance * 100); t_distance = t_distance / 100; unit = "km"; } information = place + "は、" + dir + "です。距離は,約 " + t_distance + unit + "。" + "歩いて、" + hosoku + "。"; if (comment != "") { information += comment + "。" } information_yomi = place_yomiage + "、は、" + dir + "です。距離は,約 " + t_distance + unit + "。" + "歩いて、" + hosoku + "。"; if (comment_yomiage != "") { information_yomi += comment_yomiage + "。" } android_ar.set_result(information); if (dir_A == "L") { var cat_x = Math.round(screen_width/2 - screen_width * theta_deg/horizontal_angle); }else if (dir_A == "R") { var cat_x = Math.round(screen_width/2 + screen_width * theta_deg/horizontal_angle); } cat_x -= 50; var cat_y = 200; android_ar.seImageOnscreen(-1,-1,cat_x,cat_y); } function judge_LR(theta_deg){ //android head virtual point var vh_x = 0; var vh_y = 0; if ((vc_lng >= c_lng)&&(vc_lat >= c_lat)) {//① vh_y = vc_lat; var temp = vc_lat - c_lat; vh_x = Math.pow(temp,2) / (c_lng - vc_lng) + eval(c_lng); } else if ((vc_lng >= c_lng)&&(vc_lat < c_lat)){//② vh_x = vc_lng; var temp = vc_lng - c_lng; vh_y = Math.pow(temp,2) / (c_lat - vc_lat) + eval(c_lat); } else if ((vc_lng < c_lng)&&(vc_lat < c_lat)){//③ vh_y = vc_lat; var temp = vc_lat - c_lat; vh_x = Math.pow(temp,2) / (c_lng - vc_lng) + eval(c_lng); } else if ((vc_lng < c_lng)&&(vc_lat >= c_lat)){//④ vh_x = vc_lng; var temp = vc_lng - c_lng; vh_y = Math.pow(temp,2) / (c_lat - vc_lat) + eval(c_lat); } //android head virtual point var x1 = vh_x; var y1 = vh_y; //my point var x2 = c_lng; var y2 = c_lat; //target point var x3 = target_lng; var y3 = target_lat; var ax = x3 - x2; var ay = y3 - y2; var bx = x1 - x2; var by = y1 - y2; var alpha_rad = Math.acos(((ax * bx) + (ay * by))/(Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))); var alpha_deg = Math.round(alpha_rad * (180 / Math.PI)); var dir = ""; if ((alpha_deg >= 0)&&(alpha_deg <= 90)) { if ((theta_deg >= 0)&&(theta_deg <= 90)) { dir = "L/forward"; } if ((theta_deg > 90)&&(theta_deg <= 180)) { dir = "L/behind"; } } if ((alpha_deg > 90)&&(alpha_deg <= 180)) { if ((theta_deg >= 0)&&(theta_deg <= 90)) { dir = "R/forward"; } if ((theta_deg > 90)&&(theta_deg <= 180)) { dir = "R/behind"; } } return dir; }
磁気偏角の計算
国土地理院の近似計算式
を使用
実際、実装するかどうかは未定(Androidのライブラリにあるみたいだけど...)。
2010版です。
JavaScriptだとこんな感じ。
function get_md_2010(lat,lng){ var k = 40.585; var lamda_lat = lat - 37; var lamda_lng = lng - 138; var D = k + 19.003 * lamda_lat - 6.265*lamda_lng + 0.009 * lamda_lat*lamda_lat + 0.024 * lamda_lat*lamda_lng - 0.591 * lamda_lng*lamda_lng; D = D/100; var MD = 7 + D; return MD; }
だれに頼まれたわけでもないのに、こんなやつを作ってみることにした。
万博に来たら、いろいろな情報をご提供するサービスAR。
万博のエリアに入ったら起動。
スタジアムの内外でモードを変える。
スタジアム内では、試合関連の情報を表示。
例えば、こんな感じ。
スタジアム外では施設の案内。
てな具合。
under construction
TOP
ターゲット
東京スカイツリー
通天閣
........なんで通天閣やのん?という方もいらっしゃるとは思いますが...。
そんなこと言うやつは、かかってこいや!って感じ(^^)。
TOP
久々のOpenCVネタですな。
OpenCV 2.3.1 + Android NDK
アンドロイドでOpenCV
こちらのページです
TOP