jQuery Mobile + Google Maps V3 をジェスチャーで制御
Google Maps V3をジェスチャーで制御するコードです。
基本は、mapのdraggableをfalseにして、ジェスチャーでmapをコントロール。
ジェスチャーはスワイプとサークル・ジグザグの認識の3つ。
この3つは、アルゴリズム上まったく異なる図形ってことです(まあ、見た目もまったく違いますが)。
3つを識別するには、以下の2つを検証すれば、ええんじゃないか...と。
1:開いている/閉じている
2:多い/少ない
あるいは、補助線の考え方を使えば、一回でいける?
交点が、1なら線、2なら円、それ以上なら...って。
このアルゴリズムはおいおい考えませう。
以下で紹介しているアルゴリズムのコンセプトは、こんな感じ。
ジェスチャーで取得した点座標列を使って「図形そのもの」を認識するわけではないです。
相対判断ってやつで、ベクトル角を使うのは、これって次元が増えても普通に使えるから。
それに、これを使うと「単位」を意識しなくてすみますし....。角度が「何についてのものか」は任意です。
相隣り合う3点で鋭角の個数を見る。
円の場合は、相隣り合う3点の鋭角の個数は0。
相隣り合う3点の鋭角を複数含むならzigzag
始点・構成点・終点で角度を見て、広角を持つものは円、鋭角のみ(つまり始点から見て
他の点が終点の方を向いているだけ)なら円ではない。
で、ロジックを組むときに重要なのは、点座標列は、例えばtapholdのような場合、実に無駄なデータを多く採取して、これが誤差を生みます。
これを排除する仕組みも組み込んでおく必要があります(今回は公開してないですが、ちょっと考えれば簡単)。
このアルゴリズムは、他ページでサッカーゲームのリアルタイム分析で使うことにしよう...と目論んでいます(^^)。
こういうことですね。
で、ジグザグの場合はちょっとした「コツ」というか「慣れ」が必要ですが、これもジェスチャーの要素と考えます。
【index.html】
工事中
コードのベース
【g_script.js】
工事中
コードのベース
TOP
基本は、mapのdraggableをfalseにして、ジェスチャーでmapをコントロール。
ジェスチャーはスワイプとサークル・ジグザグの認識の3つ。
この3つは、アルゴリズム上まったく異なる図形ってことです(まあ、見た目もまったく違いますが)。
3つを識別するには、以下の2つを検証すれば、ええんじゃないか...と。
1:開いている/閉じている
2:多い/少ない
あるいは、補助線の考え方を使えば、一回でいける?
交点が、1なら線、2なら円、それ以上なら...って。
このアルゴリズムはおいおい考えませう。
以下で紹介しているアルゴリズムのコンセプトは、こんな感じ。
ジェスチャーで取得した点座標列を使って「図形そのもの」を認識するわけではないです。
相対判断ってやつで、ベクトル角を使うのは、これって次元が増えても普通に使えるから。
それに、これを使うと「単位」を意識しなくてすみますし....。角度が「何についてのものか」は任意です。
相隣り合う3点で鋭角の個数を見る。
円の場合は、相隣り合う3点の鋭角の個数は0。
相隣り合う3点の鋭角を複数含むならzigzag
始点・構成点・終点で角度を見て、広角を持つものは円、鋭角のみ(つまり始点から見て
他の点が終点の方を向いているだけ)なら円ではない。
で、ロジックを組むときに重要なのは、点座標列は、例えばtapholdのような場合、実に無駄なデータを多く採取して、これが誤差を生みます。
これを排除する仕組みも組み込んでおく必要があります(今回は公開してないですが、ちょっと考えれば簡単)。
このアルゴリズムは、他ページでサッカーゲームのリアルタイム分析で使うことにしよう...と目論んでいます(^^)。
こういうことですね。
で、ジグザグの場合はちょっとした「コツ」というか「慣れ」が必要ですが、これもジェスチャーの要素と考えます。
表示サンプルとコード
【index.html】
工事中
コードのベース
<!DOCTYPE HTML> <html> <head> <title>Route Map</title> <meta name="viewport" content="width=device-width, initial-scale=1,minimum-scale=1, maximum-scale=1"> <meta charset="UTF-8"> <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"/> <style type="text/css"> // </style> <script src="http://code.jquery.com/jquery-1.6.1.min.js"></script> <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true&language=ja"></script> <script src="g_script.js"></script> <script src="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js"></script> <script type="text/javascript" charset="utf-8" src="phonegap-1.1.0.js"></script> <script type="text/javascript" charset="utf-8" src="SpeechRecognizer.js"></script> <script type="text/javascript" charset="utf-8"> //********************************************* $(document).ready(function(){ $("#map_canvas").bind("touchstart", function(event){ //地図をタップした時の振る舞いを記述 }); $("#map_canvas").bind("taphold", function(){ //地図を長押しした時の振る舞いを記述 }); }); var eventEasy_Swipe = function(event){ gesture_x = []; gesture_y = []; gesture_index = 0; //初期位置取得 var start = { time: (new Date()).getTime(), coords: [event.touches[0].clientX, event.touches[0].clientY] }; var stop = {}; var tap_move = function(event){ //指を動かしたときの途中の座標を取得しておく gesture_x[gesture_index] = event.touches[0].clientX; gesture_y[gesture_index] = event.touches[0].clientY; gesture_index++; if(!start){ return; } //最後の座標取得 stop = { time: (new Date()).getTime(), coords: [event.touches[0].clientX, event.touches[0].clientY] }; if(Math.abs(start.coords[0] - stop.coords[0]) > 10){ event.preventDefault(); } }; var tap_stop = function(){ document.getElementById('map_canvas').removeEventListener('touchmove', tap_move, false); document.getElementById('map_canvas').removeEventListener('touchend', tap_stop, false); //ジェスチャーのタイプを検出 var g_type = gestureType(); if (g_type == "circle") { // } else if (g_type == "swipe") { // } else if (g_type == "zigzag"){ // } start = stop = undefined; }; /*+++++++++++++++++++++++++++++++++*/ // document.getElementById('map_canvas').addEventListener('touchmove', tap_move, false); document.getElementById('map_canvas').addEventListener('touchend', tap_stop, false); }; function onDeviceReady() { document.getElementById('map_canvas').addEventListener('touchstart', eventEasy_Swipe, false); } </script> </head> <body onLoad="initialize()"> <!-- +++++++++++++++++++++++++++++++++++++++++++ --> <div data-role="page" id="map" data-theme="b"> <div data-role="header" id="map_header"> </div> <div data-role="content" id="map_content" style="width:100%;height:440px;"> <div id="map_canvas" style="width:0px;left:0px;height:0px;top:0px;transform-origin:'50% 50%';-moz-transform: rotate(0deg);-webkit-transform: rotate(0deg);transform:rotate(0deg);"> Google Maps </div> </div> <div data-role="footer" id="map_footer"> Adv </div> </div> </body> </html>
【g_script.js】
工事中
コードのベース
var map; var F_latlng; var gesture_x = []; var gesture_y = []; var gesture_index = 0; function initialize() { document.addEventListener("deviceready", onDeviceReady, false); // F_latlng = new google.maps.LatLng(34.6060845921693, 135.3076171875); var map_Options = { zoom: 17, center: F_latlng, mapTypeId: google.maps.MapTypeId.ROADMAP, navigationControl:false, draggable:false }; map = new google.maps.Map(document.getElementById("map_canvas"), map_Options); //------------------------------------ var pageHeight = $(document).height(); var pageWidth = $(document).width(); var new_width = pageWidth * 2; var new_height = pageHeight * 1.5; var new_top = -(new_height - pageHeight)/2; var new_left = -(new_width - pageWidth)/2; //alert(new_width + "/" + new_height + "/" + new_top + "/" + new_left); $("#map_canvas").css("height",new_height); $("#map_canvas").css("width",new_width); $("#map_canvas").css("top",new_top); $("#map_canvas").css("left",new_left); }
function get_angle(x1,y1,x2,y2,x3,y3){ var ax; var ay; var cx; var cy; ax = x1 - x2; ay = y1 - y2; cx = x3 - x2; cy = y3 - y2; var θ = Math.acos(((ax * cx) + (ay * cy)) / (Math.sqrt(ax * ax + ay * ay) * Math.sqrt(cx * cx + cy * cy))); var deg = 180 * θ / Math.PI; deg = Math.ceil(deg); return deg; }
function swipe_map(start_x,start_y,stop_x,stop_y,heading_angle){ //地図が回転している場合 //start_x,start_y:swipeスタート位置 //stop_x,stop_y:swipeストップ位置 //heading_angle:地図の回転角 var rad = heading_angle * (Math.PI / 180); var start_x_r = start_x * Math.cos(rad) - start_y * Math.sin(rad); var start_y_r = start_x * Math.sin(rad) + start_y * Math.cos(rad); var stop_x_r = stop_x * Math.cos(rad) - stop_y * Math.sin(rad); var stop_y_r = stop_x * Math.sin(rad) + stop_y * Math.cos(rad); var dis_x = -(stop_x_r - start_x_r); var dis_y = -(stop_y_r - start_y_r); // map.panBy(dis_x,dis_y); }
function gestureType(){ var gesture_type = ""; var angle_count = 0; angle_count = check_anglecount(); if (angle_count == 0) { var res = check_circle(); if(res == true){ gesture_type = "circle"; }else{ gesture_type = "swipe"; } }else{ gesture_type = "zigzag"; } return gesture_type; } function check_anglecount(){ var check_angle_count = 0; var deg; var x1; var y1; var x2; var y2; var x3; var y3; for (var i = 0;i < gesture_x.length;i++) { if (i < gesture_x.length - 3){ x1 = gesture_x[i]; y1 = gesture_y[i]; x2 = gesture_x[i + 1]; y2 = gesture_y[i + 1]; x3 = gesture_x[i + 2]; y3 = gesture_y[i + 2]; deg = get_angle(x1,y1,x2,y2,x3,y3); if (deg < 90) { check_angle_count++; } } } return check_angle_count; } function check_circle(){ var deg; var x1; var y1; var x2; var y2; var x3; var y3; // if (gesture_x.length < 4) { return false; }else{ var res = false; for (var i = 1;i < gesture_x.length - 1;i++) { if (i < gesture_x.length - 3){ x1 = gesture_x[i]; y1 = gesture_y[i]; x2 = gesture_x[0]; y2 = gesture_y[0]; x3 = gesture_x[gesture_x.length - 1]; y3 = gesture_y[gesture_x.length - 1]; deg = get_angle(x1,y1,x2,y2,x3,y3); if (deg > 90) { res = true; break; } } } return res; } }
TOP