AndroidでGoogleMapアプリ開発
第1回 表示
第2回 表示形式 衛星、地図、地形図
第3回 マーカーと直線
第4回 googleMapのエラー
第5回 PlaceAPIとマーカーのセット
第6回 現在地 GPSの実装
第7回 トラッキング
第8回 様々な技法
第4回までにおいてgoogleMapにおける一通りの基本操作を説明しました。今回はgooglePlaceAPIのJSONを用いて、例えば近くのコンビニやATMを表示させるといったことを実装させる方法について説明します。
Volleyを用いたJSON解析
Volley実装方法についてはこちらを参考にしてください。
前準備として、placeAPIを有効化したAPI_KEYは事前に用意してください。
今回はデータについていわゆるカプセル化を行って管理をします。
googlePlaceAPIでは以下のようなデータを取得することができます。
PlaceAPIのJSONデータの概要
西新宿の日本興亜損保ビルを中心とし、ATMについて検索をかけた結果です。
{ "html_attributions" : [], "next_page_token" : "CqQCGQEAAP89vpuZK-aK4TRM2J_fFZgFVNqS16IZSY925f9PRNB4trpcgrj4MXWUlFcoXDlYnZIdJMWZLcWEq_DcumWsqWUw4uUDFXnWc1WT6zXZUYS3JarLnMZfxmxtAGTSNcVgd3v3CTk922ScHZYRmfkNrDw8neJApUjODFCgVvYd1cTgt5ZlTKz9bXIBPTmB2bPASmZcbmjFBm1ood3NSOfnBCI9QEBXTi8nzz77eXT2_yDJelSdzv358bxi5yd0PRQLwjH03ZGSJsIMKkHbhz4slajmuPewnByKT2eJatjLD_L_oZtFqXiw0Q-R97FGqxemxMt5tqAAsyZU2u2FwPSMEsp3Kw38UJEOKR4Ogy9kGspWjkBAYzMXMUMNByBHdx0TFxIQGF0hZLt5v7o4VBU9UF_vaBoUar7SScS-2P0oopbb75rQ8iJ_Fp8", "results" : [ { "geometry" : { "location" : { "lat" : 35.6909271, "lng" : 139.6970882 }, "viewport" : { // 略 } }, "icon" : "https://maps.gstatic.com/mapfiles/place_api/icons/bank_dollar-71.png", "id" : "1153521cf0b0c1296be0295704accab2f6d6930b", "name" : "三菱東京UFJ銀行 新宿中央支店", "opening_hours" : { "open_now" : false, "weekday_text" : [] }, "photos" : [ { // 略 } ], "place_id" : "ChIJaT9ZpdaMGGARVrldL8-_2rI", "rating" : 3.5, "reference" : "CmRSAAAA2oBF2YmyiFo72MQCj7j8mSR99tXfJuQ-QYodmO8GKYFYipr3zP2ndrR4yH50GvSFNJA4K1CUnjZ6OI8MthYK0tA06XUDHhrgEJ6g4aEy_kd36Hs9erYk42S_eXVvLkT6EhBhSu8VliI5Mmk0q8jWXhF_GhSsJPg5HkFIPclkscRPVJB6GCKu6Q", "scope" : "GOOGLE", "types" : [ "bank", "atm", "finance", "point_of_interest", "establishment" ], "vicinity" : "新宿区西新宿1丁目8−1" }, { "geometry" : { //以下略
合計で20件表示されます。
データのカプセル化
今回取得する情報は
・location(緯度経度)
・name(名称)
・open_now(現在開業中かどうか(信用性は?))
・vicinity(住所)
とします。それではカプセル化します。
public class JsonItem { private LatLng latLng; private String nameString; private int openFlag; private String addressString; public JsonItem(LatLng latLng,String nameString, int openFlag,String addressString){ this.latLng = latLng; this.nameString = nameString; this.openFlag = openFlag; this.addressString = addressString; } public LatLng getLatLng() { return latLng; } public void setLatLng(LatLng latLng) { this.latLng = latLng; } public String getNameString() { return nameString; } public void setNameString(String nameString) { this.nameString = nameString; } public int getOpenFlag() { return openFlag; } public void setOpenFlag(int openFlag) { this.openFlag = openFlag; } public String getAddressString() { return addressString; } public void setAddressString(String addressString) { this.addressString = addressString; } }
とします。openFlagがbooleanでない理由はこの値には[開店中、閉店中、データなし]の3種類があるためです。
取得
それでは実際に取得します。事前にVolleyの準備を行ってください。(当ページの最初にあるリンク参照)
setJsonListというメソッドを作ります。hereLLは検索座標、mは範囲(メートル)、typeArrayは取得するタイプ(ATMなど)を配列として渡します。
タイプについてはこちらを参照
タイプを2つ入れれば40この値が入ることになります。
private void setJsonList(LatLng hereLL,int m,String[] typeArray){ final List<JsonItem> childItem = new ArrayList<JsonItem>(); String API = getResources().getString(R.string.google_maps_key); double centerLat = hereLL.latitude; double centerLng = hereLL.longitude; for(int j=0 ; j<typeArray.length ; j++){ //指定タイプごとにurlを用意します。2つであれば2回実行することになります。 final String typeString = typeArray[j]; String addressUrl = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location="+centerLat+","+centerLng+"&radius="+m+"&type="+typeString+"&language="+language+"&key="+API; JsonObjectRequest movieReq = new JsonObjectRequest(Request.Method.GET, addressUrl, null, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { try { for(int i = 0; i<response.getJSONArray("results").length() ; i++) { LatLng latLng = new LatLng( Double.parseDouble(response.getJSONArray("results") .getJSONObject(i).getJSONObject("geometry") .getJSONObject("location").getString("lat")) ,Double.parseDouble(response.getJSONArray("results") .getJSONObject(i).getJSONObject("geometry") .getJSONObject("location").getString("lng")) ); /* LatLngで扱いますから取得した緯度、経度をLatLngとします。 */ int openFlag; if(response.getJSONArray("results").getJSONObject(i) .has("opening_hours")){ String openFlagString = response.getJSONArray("results").getJSONObject(i).getJSONObject("opening_hours").getString("open_now"); if (openFlagString.equals("true")) openFlag = 0; else openFlag = 1; }else{ openFlag = -1; } /* opening_hours自体が存在するかどうかhas()で確認を行い、値を取得します。 true,falseを取得するわけですがStringですのでequalsで判定し、trueで0としました。 has()がfalse、つまり存在しない場合は-1とします。 レーティング情報についても同様に存在判定を行います。存在しない場合は同様に-1とします */ float ratingFloat; if(response.getJSONArray("results").getJSONObject(i) .has("rating")){ ratingFloat = Float.parseFloat(response.getJSONArray("results").getJSONObject(i).getString("rating")); }else{ ratingFloat = -1; } /* LIST<JsonItem>のchildItemに値を入れていきます。 */ childItem.add(new JsonItem( latLng , response.getJSONArray("results").getJSONObject(i) .getString("name") , openFlag , response.getJSONArray("results").getJSONObject(i) .getString("vicinity") )); int nowNumber = childItem.size()-1; //現時点でのchildItemの最後の値を取得するため、nowNumberを用意 //マーカーを地図にセットしていきます。 marker = mMap.addMarker(new MarkerOptions() .position(childItem.get(nowNumber).getLatLng()) .icon(BitmapDescriptorFactory .defaultMarker(BitmapDescriptorFactory.HUE_YELLOW)) .title(childItem.get(nowNumber).getNameString()) .snippet(childItem.get(nowNumber).getAddressString())); mMarker.add(marker); } } catch (JSONException e) { Log.d("Log0",e.getMessage()); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { VolleyLog.d("Log0", "Error: " + error.getMessage()); } }); AppController.getInstance().addToRequestQueue(movieReq); } }
非同期処理ですから結果を表示する際にはonResponse内に記述しました。
また、冒頭で以下のように定義を行っています。
private GoogleMap mMap; private CameraPosition cameraPosition; private String language="JA"; private Marker marker; private java.util.Set<Marker> mMarker = new java.util.HashSet<>();
実はさほど難しいことではないのですが、placeAPIには回数制限(といってもクレジットカードでの確認を行えば一日15万件)がありますので、マップが動くたびに取得するような方法にしてしまいますとあっという間に上限になってしまうかもしれませんし、動作自体が非常に重くなります。その点は十分ご注意ください。
実装する際はonMapReady()に
String[] placeTypeArray = {"atm","convenience_store"}; setJsonList(mMap.getCameraPosition().target,1000,placeTypeArray);
という具合となります。
マーカーの詳しい扱い方については以下の記事を参考にしてください