AR(拡張現実)をやってみましょう。

ARといってもいろいろなやり方がありますが、ここではマーカーレスで、「案内や紹介」をやってみます。

StreetViewでハウステンボスや旭山動物園を歩き回るのに使っている方法をARに適用してみます。

表示サンプルとコード

工事中

コードを若干変更。 初期画面はこんな感じ。現行のコードでは、Mapsを一度アクティブにする必要があるみたい(この辺はもう少し調べてみます)。

初期化画面を変更しました(splash)。





この初期画面が表示されると、即カメラ画面へ遷移します。

New
レイアウトを変更しました。





:ドラッグで移動
:JavaScriptから制御、初期画面では見えませんが、位置を与えることで表示



音声読み上げ用のボタンと、Google Maps表示用のボタンも追加。

Google Mapsのレイアウトも変更しました。

ところで、「なんでMapsなん?ARだけでええんちゃうん」という方もいらっしゃるようですが...。
WisteriaHillは一応地図のサイトってこともありますし、人間、どうしたって「確認」したくなる生き物。 というわけで、地図を入れてます。





音声認識コードを追加。認識用のコードはJavaですが、結果はJavaScriptに渡しています。







New
方位角(heading)取得コードを入れてみました。

5度毎の変化をモニター(まともに取ると、描画時にアボートする可能性があるもんで)。




New
カメラのファインダーの向きを地図上で線で示してみました(だいたいの向きですが...)。
カメラを向けると、その方向に線が動きます。




プロジェクトのソースはこちら

ソースには情報採取・表示のコードはまだ記述されていません、ちょい待ってください。

サーバーサイドのソースはこのページの下の方に記述されてます、よろしくね。

プロジェクトソースに、アイコンを入れました、こんな感じ。

注意

Androidのアイコンは矩形なものが多いので、視認性を差異化した方がいいかも。
透過pngを使うと、結構、他のものと区別できますよ、それと色合いは明るいやつ。



AndroidManifest.xmlを編集して、いろいろ試してみてください。


例によって、Eclipse + Android SDKの環境を使います。
アットマーク・アイティのページを参考にさせていただきました。

こちらのぺージでは、いろいろなコードがてんこ盛りでしたので、ARするのに必要な部分のみ抽出してみました。

今回のプロジェクト名は「AR_GuideII」。
JavascriptとJavaのハイブリッドです。(target:Android 2.3.3)
画像では、alertでメッセージを表示していますが、最終的には音声出力になります。
【AndroidManifest.xml】

<?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>

【AR_GuideIIActivity.java】

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 {
            //
            
        }
    }

}


【CameraPreview.java】

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;
    

    
}

【Overlay.java】

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;
        }
    }
}



【index.html】

<!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>



【script.js】

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); }

カメラの向き

【call_mapdb.php】

こんな感じで方向を見ます。





結果。





<?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);
    
?>


TOP



カメラの中での角度

地図上の地物がカメラに映りこんでいる時、その物体はカメラの中でどの位の角度を持つか?...ていうのをやってみました。

ここでは、”(地物)物体”とは、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 θ;

}


万博記念競技場の場合は、こんな風に見えます。

わんちゃんの画像は、θの水平方向に投影された位置です。赤いマーカーは視線の方向。





どうも表示の値が変じゃ?と思ったら、地図のカメラ方向の表示を確認してみてください。
この青い線。





この線が、意味なく振れている場合は、センサーを調整する必要があります。

キャリブレーションの仕方はこちら

TOP



カメラと対象物の位置関係を調べる

「カメラの中での角度」の別のロジックです。

こんな感じ。





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;
    
    
}


TOP

磁気偏角

磁気偏角の計算

国土地理院の近似計算式 を使用

実際、実装するかどうかは未定(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;
    
}


TOP

万博 AR for Android

だれに頼まれたわけでもないのに、こんなやつを作ってみることにした。

万博に来たら、いろいろな情報をご提供するサービスAR。





万博のエリアに入ったら起動。

スタジアムの内外でモードを変える。

スタジアム内では、試合関連の情報を表示。
例えば、こんな感じ。







スタジアム外では施設の案内。

てな具合。

under construction

TOP

日本が世界に誇る2大タワーへの道案内 AR for Android

ターゲット

東京スカイツリー
通天閣


........なんで通天閣やのん?という方もいらっしゃるとは思いますが...。
そんなこと言うやつは、かかってこいや!って感じ(^^)。




TOP

アンドロイドでOpenCV(画像認識)

久々のOpenCVネタですな。

OpenCV 2.3.1 + Android NDK

アンドロイドでOpenCV

こちらのページです

TOP