Google Maps Android V2 でルート検索は、まだネイティブでサポートされていません。

Google Directions API(Web Service)とV2のポリライン描画を使ってルート検索をやってみます。

こんな感じ。

Driving


Walking







アプリについて

コードについて




アプリについて

初期画面



地図上をタップしてスタート地点を設定



再度タップして到着地点を設定

設定と同時にルート検索が実行されます(初期モードは運転)





マーカーをタップすると、位置の住所情報が表示されます



ルートの詳細情報はメニュの「Info」





トラベルモードを変更できます、メニュの「Mode」



デフォルトは「運転」ですが「歩行」に変更してみます。



変更と同時に、リルートが実行されます。






さらにタップすれば、スタート地点が再設定されます。




コード(抜粋)

Google Maps Android API V2を使用する場合のコードはこちらを参照

Google Maps Android API V2を使ってみる

Google Maps Android API V2覚書(地図処理系)

Google Maps Android API V2覚書 その2(画像処理系)


ルート検索

以下のコードではメニュ関連のものは省いてます


activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >
    
    <fragment
    android:id="@+id/map"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    class="com.google.android.gms.maps.SupportMapFragment" />
</RelativeLayout>



MainActivity.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.json.JSONObject;

import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapClickListener;

import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;
import com.google.android.gms.maps.CameraUpdateFactory;

import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener;
import com.google.android.gms.common.GooglePlayServicesUtil;
import import android.app.ProgressDialog;

public class MainActivity  extends FragmentActivity {

    GoogleMap gMap;
    private static final int MENU_A = 0;
    private static final int MENU_B = 1;
    private static final int MENU_c = 2;

    public static String posinfo = "";
    public static String info_A = "";
    public static String info_B = "";
    ArrayList<LatLng> markerPoints;
    
    public static MarkerOptions options;
    
    public ProgressDialog progressDialog;
    
    public String travelMode = "driving";//default
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
    //プログレス
        progressDialog = new ProgressDialog(this);
        progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        progressDialog.setMessage("検索中だす......");
        progressDialog.hide();
        
        
        //初期化
        markerPoints = new ArrayList<LatLng>();
        
    
        SupportMapFragment mapfragment = (SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.map);
        
    
        gMap = mapfragment.getMap();
        
    //初期位置
        LatLng location = new LatLng(34.802556297454004,135.53884506225586); 
        gMap.moveCamera(CameraUpdateFactory.newLatLngZoom(location, 17));
        
        if(gMap!=null){
      
            gMap.setMyLocationEnabled(true);

      //クリックリスナー
            gMap.setOnMapClickListener(new OnMapClickListener() {
                @Override
                public void onMapClick(LatLng point) {
                    
          //3度目クリックでスタート地点を再設定
                    if(markerPoints.size()>1){
                        markerPoints.clear();
                        gMap.clear();
                    }

                    
                    markerPoints.add(point);
                    
                    
                    options = new MarkerOptions();
                  options.position(point);
                    
          
                    if(markerPoints.size()==1){
                        //options.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
                        options.icon(BitmapDescriptorFactory.fromResource(R.drawable.green));
                        options.title("A");
                        
                        
                    }else if(markerPoints.size()==2){
                        //options.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED));
                        options.icon(BitmapDescriptorFactory.fromResource(R.drawable.red));
                        options.title("B");
                        
                        
                    }
                    
                    
                    gMap.addMarker(options);
                    
                    
                    gMap.setOnMarkerClickListener(new OnMarkerClickListener() {
                        @Override
                        public boolean onMarkerClick(Marker marker) {
                    // TODO Auto-generated method stub
                    
                    
                    String title = marker.getTitle();
                    if (title.equals("A")){
                        marker.setSnippet(info_A);
                    
                    }else if (title.equals("B")){
                        marker.setSnippet(info_B);
                    }
                    
                    
                    return false;
                }
                  });
                    
                    
                    
                    if(markerPoints.size() >= 2){
            //ルート検索
                        routeSearch();
                    }
                }
            });
        }
    }
    
    
    private void routeSearch(){
        progressDialog.show();
        
        LatLng origin = markerPoints.get(0);
        LatLng dest = markerPoints.get(1);
    
        
        String url = getDirectionsUrl(origin, dest);
    
        DownloadTask downloadTask = new DownloadTask();
    
        
        downloadTask.execute(url);
        
    }

    private String getDirectionsUrl(LatLng origin,LatLng dest){

        
        String str_origin = "origin="+origin.latitude+","+origin.longitude;

        
        String str_dest = "destination="+dest.latitude+","+dest.longitude;

        
        String sensor = "sensor=false";

        //パラメータ
        String parameters = str_origin+"&"+str_dest+"&"+sensor + "&language=ja" + "&mode=" + travelMode;

        //JSON指定
        String output = "json";

        
        String url = "https://maps.googleapis.com/maps/api/directions/"+output+"?"+parameters;

        return url;
    }
    
    private String downloadUrl(String strUrl) throws IOException{
        String data = "";
        InputStream iStream = null;
        HttpURLConnection urlConnection = null;
        try{
            URL url = new URL(strUrl);

            
            urlConnection = (HttpURLConnection) url.openConnection();

            
            urlConnection.connect();

            
            iStream = urlConnection.getInputStream();

            BufferedReader br = new BufferedReader(new InputStreamReader(iStream));

            StringBuffer sb = new StringBuffer();

            String line = "";
            while( ( line = br.readLine()) != null){
                sb.append(line);
            }

            data = sb.toString();

            br.close();

        }catch(Exception e){
            Log.d("Exception while downloading url", e.toString());
        }finally{
            iStream.close();
            urlConnection.disconnect();
        }
        return data;
    }

    
    private class DownloadTask extends AsyncTask<String, Void, String>{
    //非同期で取得
        
        @Override
        protected String doInBackground(String... url) {

            
            String data = "";

            try{
                // Fetching the data from web service
                data = downloadUrl(url[0]);
            }catch(Exception e){
                Log.d("Background Task",e.toString());
            }
            return data;
        }

        
        // doInBackground()
        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
    
            ParserTask parserTask = new ParserTask();
      
           
            parserTask.execute(result);
        }
    }

    /*parse the Google Places in JSON format */
    private class ParserTask extends AsyncTask<String, Integer, List<List<HashMap<String,String>>> >{

        
        @Override
        protected List<List<HashMap<String, String>>> doInBackground(String... jsonData) {

            JSONObject jObject;
            List<List<HashMap<String, String>>> routes = null;

            try{
                jObject = new JSONObject(jsonData[0]);
                parseJsonpOfDirectionAPI parser = new parseJsonpOfDirectionAPI();

                
                routes = parser.parse(jObject);
            }catch(Exception e){
                e.printStackTrace();
            }
            return routes;
        }

        //ルート検索で得た座標を使って経路表示
        @Override
        protected void onPostExecute(List<List<HashMap<String, String>>> result) {
            
            
            ArrayList<LatLng> points = null;
            PolylineOptions lineOptions = null;
            MarkerOptions markerOptions = new MarkerOptions();
            
            if(result.size() != 0){
                
                for(int i=0;i<result.size();i++){
                    points = new ArrayList<LatLng>();
                    lineOptions = new PolylineOptions();
    
                    
                    List<HashMap<String, String>> path = result.get(i);
    
                    
                    for(int j=0;j<path.size();j++){
                        HashMap<String,String> point = path.get(j);
    
                        double lat = Double.parseDouble(point.get("lat"));
                        double lng = Double.parseDouble(point.get("lng"));
                        LatLng position = new LatLng(lat, lng);
    
                        points.add(position);
                    }
    
                    //ポリライン
                    lineOptions.addAll(points);
                    lineOptions.width(10);
                    lineOptions.color(0x550000ff);
                    
                }
            
                //描画
                gMap.addPolyline(lineOptions);
            }else{
                gMap.clear();
                Toast.makeText(MainActivity.this, "ルート情報を取得できませんでした", Toast.LENGTH_LONG).show();
            }
            progressDialog.hide();
            
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        //getMenuInflater().inflate(R.menu.main, menu);
        menu.add(0, MENU_A,   0, "Info");
        menu.add(0, MENU_B,   0, "Legal Notices");
        menu.add(0, MENU_c,   0, "Mode");
        return true;
    }

    public boolean onOptionsItemSelected(MenuItem item) {
        switch ( item.getItemId() )
        {
            case MENU_A:
                //show_mapInfo();
                return true;
                
            case MENU_B:
                //Legal Notices(免責事項)
                
                String LicenseInfo = GooglePlayServicesUtil.getOpenSourceSoftwareLicenseInfo(getApplicationContext());
                AlertDialog.Builder LicenseDialog = new AlertDialog.Builder(MainActivity.this);
                LicenseDialog.setTitle("Legal Notices");
                LicenseDialog.setMessage(LicenseInfo);
                LicenseDialog.show();
                
                return true;
            
            case MENU_c:
                //show_settings();
                return true;
                
            }
        return false;
    }
    
    //リ・ルート検索
    private void re_routeSearch(){
        progressDialog.show();
        
        LatLng origin = markerPoints.get(0);
        LatLng dest = markerPoints.get(1);
        
        //
        gMap.clear();
        
        //マーカー
        //A
        options = new MarkerOptions();
        options.position(origin);
        options.icon(BitmapDescriptorFactory.fromResource(R.drawable.green));
        options.title("A");
        options.draggable(true);
        gMap.addMarker(options);
        //B
        options = new MarkerOptions();
        options.position(dest);
        options.icon(BitmapDescriptorFactory.fromResource(R.drawable.red));
        options.title("B");
        options.draggable(true);
        gMap.addMarker(options);
        
        
        String url = getDirectionsUrl(origin, dest);

        DownloadTask downloadTask = new DownloadTask();

        
        downloadTask.execute(url);
        
    }
}



parseJsonpOfDirectionAPI.java

JSONPをパース

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.google.android.gms.maps.model.LatLng;

import android.util.Log;

public class parseJsonpOfDirectionAPI {
    
    MainActivity ma;
    
    public List<List<HashMap<String,String>>> parse(JSONObject jObject){
        String temp = "";
        
        List<List<HashMap<String, String>>> routes = new ArrayList<List<HashMap<String,String>>>() ;
        JSONArray jsonRoutes = null;
        JSONArray jsonLegs = null;
        JSONArray jsonSteps = null;

        try {

            jsonRoutes = jObject.getJSONArray("routes");
            
            for(int i=0;i<jsonRoutes.length();i++){
                jsonLegs = ( (JSONObject)jsonRoutes.get(i)).getJSONArray("legs");
                
                //スタート地点・住所
                String s_address = (String)((JSONObject)(JSONObject)jsonLegs.get(i)).getString("start_address");
                
                ma.info_A = s_address;
                
        //到着地点・住所
                String e_address = (String)((JSONObject)(JSONObject)jsonLegs.get(i)).getString("end_address");
                
                ma.info_B = e_address;
                
                String distance_txt = (String)((JSONObject)((JSONObject)jsonLegs.get(i)).get("distance")).getString("text");
                
                temp += distance_txt + "<br><br>";
                
                String distance_val = (String)((JSONObject)((JSONObject)jsonLegs.get(i)).get("distance")).getString("value");
                
                temp += distance_val + "<br><br>";
                
                List path = new ArrayList<HashMap<String, String>>();

                
                for(int j=0;j<jsonLegs.length();j++){
                    jsonSteps = ( (JSONObject)jsonLegs.get(j)).getJSONArray("steps");

                    
                    for(int k=0;k<jsonSteps.length();k++){
                        String polyline = "";
                        polyline = (String)((JSONObject)((JSONObject)jsonSteps.get(k)).get("polyline")).get("points");
                        
                        
                        String instructions = (String)((JSONObject)(JSONObject)jsonSteps.get(k)).getString("html_instructions");
                        String duration_value = (String)((JSONObject)((JSONObject)jsonSteps.get(k)).get("duration")).getString("value");
                        String duration_txt = (String)((JSONObject)((JSONObject)jsonSteps.get(k)).get("duration")).getString("text");
                        
                        temp += instructions + "/" + duration_value + " m /" + duration_txt + "<br><br>";
                        
                        
                        List<LatLng> list = decodePoly(polyline);

                        
                        for(int l=0;l<list.size();l++){
                            HashMap<String, String> hm = new HashMap<String, String>();
                            hm.put("lat", Double.toString(((LatLng)list.get(l)).latitude) );
                            hm.put("lng", Double.toString(((LatLng)list.get(l)).longitude) );
                            path.add(hm);
                        }
                    }
        //ルート座標
                routes.add(path);
            }
            
        //ルート情報
            ma.posinfo = temp;
        }

    } catch (JSONException e) {
        e.printStackTrace();
    }catch (Exception e){
    }

    return routes;
    }
    
  //座標データをデコード
    private List<LatLng> decodePoly(String encoded) {

        List<LatLng> poly = new ArrayList<LatLng>();
        int index = 0, len = encoded.length();
        int lat = 0, lng = 0;

        while (index < len) {
            int b, shift = 0, result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lat += dlat;

            shift = 0;
            result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lng += dlng;

            LatLng p = new LatLng((((double) lat / 1E5)),
                (((double) lng / 1E5)));
            poly.add(p);
        }

        return poly;
    }
    
    
}



Directions APIで取得したデータの例:JSON

{
  "status": "OK",
  "routes": [ {
    "summary": "I-40 W",
    "legs": [ {
      "steps": [ {
        "travel_mode": "DRIVING",
        "start_location": {
          "lat": 41.8507300,
          "lng": -87.6512600
        },
        "end_location": {
          "lat": 41.8525800,
          "lng": -87.6514100
        },
        "polyline": {
          "points": "a~l~Fjk~uOwHJy@P"
        },
        "duration": {
          "value": 19,
          "text": "1 min"
        },
        "html_instructions": "Head \u003cb\u003enorth\u003c/b\u003e on \u003cb\u003eS Morgan St\u003c/b\u003e toward \u003cb\u003eW Cermak Rd\u003c/b\u003e",
        "distance": {
          "value": 207,
          "text": "0.1 mi"
        }
      },
      ...
      ... additional steps of this leg
    ...
    ... additional legs of this route
      "duration": {
        "value": 74384,
        "text": "20 hours 40 mins"
      },
      "distance": {
        "value": 2137146,
        "text": "1,328 mi"
      },
      "start_location": {
        "lat": 35.4675602,
        "lng": -97.5164276
      },
      "end_location": {
        "lat": 34.0522342,
        "lng": -118.2436849
      },
      "start_address": "Oklahoma City, OK, USA",
      "end_address": "Los Angeles, CA, USA"
    } ],
    "copyrights": "Map data ©2010 Google, Sanborn",
    "overview_polyline": {
      "points": "a~l~Fjk~uOnzh@vlbBtc~@tsE`vnApw{A`dw@~w\\|tNtqf@l{Yd_Fblh@rxo@b}@xxSfytAblk@xxaBeJxlcBb~t@zbh@jc|Bx}C`rv@rw|@rlhA~dVzeo@vrSnc}Axf]fjz@xfFbw~@dz{A~d{A|zOxbrBbdUvpo@`cFp~xBc`Hk@nurDznmFfwMbwz@bbl@lq~@loPpxq@bw_@v|{CbtY~jGqeMb{iF|n\\~mbDzeVh_Wr|Efc\\x`Ij{kE}mAb~uF{cNd}xBjp]fulBiwJpgg@|kHntyArpb@bijCk_Kv~eGyqTj_|@`uV`k|DcsNdwxAott@r}q@_gc@nu`CnvHx`k@dse@j|p@zpiAp|gEicy@`omFvaErfo@igQxnlApqGze~AsyRzrjAb__@ftyB}pIlo_BflmA~yQftNboWzoAlzp@mz`@|}_@fda@jakEitAn{fB_a]lexClshBtmqAdmY_hLxiZd~XtaBndgC"
    },
    "warnings": [ ],
    "waypoint_order": [ 0, 1 ],
    "bounds": {
      "southwest": {
        "lat": 34.0523600,
        "lng": -118.2435600
      },
      "northeast": {
        "lat": 41.8781100,
        "lng": -87.6297900
      }
    }
  } ]
}


インストール

Target:Android 2.3.3 or later

動作確認実機:Xperia acro(docomo) Android 2.3.4



アプリをQRコードからインストールする方法はコチラを参照


タブレットの場合

QRコードでダウンロードすると、Downloadフォルダー内でzipファイルになってインストールできない状態の場合があります。

その場合は、拡張子のzipをapkに変更してファイル名をタップすればインストール用のダイアログが出ますので、そこからインストールしてください。