Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Google Maps Search Nearby: Displaying Nearby Places using Google Places API and Google Maps API V2 In this practical we will learn how to find nearby places in Google Maps. We will first see how to get current location of user and then will add marker on nearby places like Restaurants, Hospitals, Schools etc. Creating new project Please follow following steps: 1. Open Android Studio and make a new project with name “Google Maps Search Nearby. 2. Click Next and choose android version Lollipop. Again Click Next and Choose Google Maps Activity (as shown in following pic). 1. Leave all things remaining same and Click Finish. Now you will be able to see three files: 1. google_maps_api.xml 2. MapsActivity.java 3. AndroidManifest.xml 1|Page Configuring Google Play Services Open google_maps_api.xml. Here you will find a lot of information along with a link. Copy-Paste this link in your web browser. Also, make a Gmail account through which you will configure google play services. Now at the browser choose Create a Project and Click Continue. Following screen will be displayed: 2|Page Create your key by clicking Create API key. Click on RESTRICT KEY. 3|Page Click on SAVE. Now a key will be created that you shall copy and paste in google_maps_api.xml. Copy paste it in place where YOUR_KEY_HERE is written: Code inside google_maps_api.xml is complete. Code Inside AndroidManifest.xml: If you go inside AndroidManifest.xml then this key will be displayed in meta tags. Here you need to add permissions for accessing location of device. The required permission should be as follows: ACCESS_NETWORK_STATE – To check network state i.e if we are connected to any network or not. INTERNET – If we are connected to Internet or not. ACCESS_COARSE_LOCATION – To determine user’s location using WiFi and mobile. It will give us approximate location. ACCESS_FINE_LOCATION – To determine user’s location using GPS. It will give us precise location. OpenGL ES V2 – Required for Google Maps V2 Finally, our code inside AndroidManifest.xml will be as follows: 4|Page <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.se245547.googlemapssearchnearby"> // This should match with your username <!-The ACCESS_COARSE/FINE_LOCATION permissions are not required to use Google Maps Android API v2, but you must specify either coarse or fine location permissions for the 'MyLocation' functionality. --> <uses-permission android:name="com.example.se245547.permission.MAPS_RECEIVE" /> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <!-The API key for Google Maps-based APIs is defined as a string resource. (See the file "res/values/google_maps_api.xml"). Note that the API key is linked to the encryption key used to sign the APK. You need a different API key for each encryption key, including the release key that is used to sign the APK for publishing. You can define the keys for the debug and release targets in src/debug/ and src/release/. --> <meta-data android:name="com.google.android.geo.API_KEY" android:value="@string/google_maps_key" /> <activity android:name=".MapsActivity" android:label="@string/title_activity_maps"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> Code inside activity_maps.xml: Here we will add three buttons each for Restaurant, Hospitals and Schools such that when user clicks on Restaurant then markers will be added on nearby restaurants. Similarly, in case of other two buttons. For this, we will use FrameLayout. Refer below code: 5|Page activity_maps.xml <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" xmlns:android="http://schemas.android.com/apk/res/android"> <fragment xmlns:android="http://schemas.android.com/apk/res/android" xmlns:map="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/map" android:name="com.google.android.gms.maps.SupportMapFragment" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.se245547.googlemapssearchnearby.MapsActivity" /> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <Button android:id="@+id/btnRestaurant" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Nearby Restaurants" /> <Button android:id="@+id/btnHospital" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Nearby Hospitals" /> <Button android:id="@+id/btnSchool" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Nearby Schools" /> </LinearLayout> </FrameLayout> Note: Please see your build.gradle file. It should have following code: dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0' compile 'com.google.android.gms:play-services:9.2.1' } Third line compile 'com.google.android.gms:play-services:9.2.1' is responsible for inserting Google Play Services. Please make sure this line is present in build.gradle MapsActivity.java Code of Google Maps Search Nearby App This is the main part of our code. We will divide it into parts to get a grasp on it. Here we won’t discuss code related to getting current user location. We will divide our project i.e. Google Maps Search Nearby into different classes so that user can easily debug code. Here we will make three classes 6|Page apart from MapsActivity.java i.e. GetNearbyPlacesData.java, DataParser.java and DownloadUrl.java. First of all, we will check if Google Play Services available or not in onCreate() function of MapsActivity.java. For that we will use function CheckGooglePlayServices() private boolean CheckGooglePlayServices() { GoogleApiAvailability googleAPI = GoogleApiAvailability.getInstance(); int result = googleAPI.isGooglePlayServicesAvailable(this); if(result != ConnectionResult.SUCCESS) { if(googleAPI.isUserResolvableError(result)) { googleAPI.getErrorDialog(this, result, 0).show(); } return false; } return true; } GoogleApiAvailability is the Helper class for verifying that the Google Play services APK is available and up-to-date on android device. If result is ConnectionResult.SUCCESS then connection was successful otherwise, we will return false. Inserting callbacks: We need to use following callback in implements portion: 1. GoogleApiClient.ConnectionCallbacks: This callback will have a public function onConnected() which will be called whenever device is connected and disconnected. 2. GoogleApiClient.OnConnectionFailedListener: Provides callbacks for scenarios that result in a failed attempt to connect the client to the service. Whenever connection is failed onConnectionFailed() will be called. 3. LocationListener: This callback will be called whenever there is change in location of device. Function onLocationChanged() will be called. public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener { Now comes the heart of our Google Maps Search Nearby code i.e. onMapReady() function. Here we will first build a Google API Client and then enable current user location using mMap.setMyLocationEnabled(true); @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); //Initialize Google Play Services if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 7|Page if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { buildGoogleApiClient(); mMap.setMyLocationEnabled(true); } } else { buildGoogleApiClient(); mMap.setMyLocationEnabled(true); } } Define a new function protected synchronized void buildGoogleApiClient() add following code: protected synchronized void buildGoogleApiClient() { mGoogleApiClient = new GoogleApiClient.Builder(this) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(LocationServices.API) .build(); mGoogleApiClient.connect(); } Add following code inside onConnected(): @Override public void onConnected(@Nullable Bundle bundle) { mLocationRequest = new LocationRequest(); mLocationRequest.setInterval(1000); mLocationRequest.setFastestInterval(1000); mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this); } } Define onLocationChanged() and add following code in it: @Override public void onLocationChanged(Location location) { Log.d("onLocationChanged", "entered"); mLastLocation = location; if (mCurrLocationMarker != null) { mCurrLocationMarker.remove(); } //Place current location marker latitude = location.getLatitude(); longitude = location.getLongitude(); LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude()); MarkerOptions markerOptions = new MarkerOptions(); markerOptions.position(latLng); markerOptions.title("Current Position"); markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MA GENTA)); mCurrLocationMarker = mMap.addMarker(markerOptions); 8|Page //move map camera mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng)); mMap.animateCamera(CameraUpdateFactory.zoomTo(11)); Toast.makeText(MapsActivity.this,"Your Current Location", Toast.LENGTH_LONG).show(); Log.d("onLocationChanged", String.format("latitude:%.3f longitude:%.3f",latitude,longitude)); //stop location updates if (mGoogleApiClient != null) { LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this); Log.d("onLocationChanged", "Removing Location Updates"); } Log.d("onLocationChanged", "Exit"); } Requesting Location Permission Application will not be granted any permission at installation time. Instead, application has to ask user for a permission one-by-one at runtime. So now add following code: public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99; public boolean checkLocationPermission(){ if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // Asking user if explanation is needed if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) { // Show an explanation to the user *asynchronously* -- don't block // this thread waiting for the user's response! After the user // sees the explanation, try again to request the permission. //Prompt the user once explanation has been shown ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, MY_PERMISSIONS_REQUEST_LOCATION); } else { // No explanation needed, we can request the permission. ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, MY_PERMISSIONS_REQUEST_LOCATION); } return false; } else { return true; } } Handling permission request response A dialog box is presented whenever any App requests permissions. When the user responds, the system invokes app’s onRequestPermissionsResult() method, passing it the user response. Our app has to override this method to find out whether the permission was granted. Add below code. 9|Page @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case MY_PERMISSIONS_REQUEST_LOCATION: { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // permission was granted. Do the // contacts-related task you need to do. if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { if (mGoogleApiClient == null) { buildGoogleApiClient(); } mMap.setMyLocationEnabled(true); } } else { // Permission denied, Disable the functionality that depends on this permission. Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show(); } return; } // other 'case' lines to check for other permissions this app might request. // You can add here other case statements according to your requirement. } } These steps we have already discussed in our last week tutorial. So now we will directly refer to the working of buttons and how to get nearby places using it. We will explain here functionality of Nearby Restaurants button. Rest of the two buttons will share same functionality except string passed will be Hospital or School according to button clicked. Finding nearby Restaurants on Google Maps We will start its implementation by setting setOnClickListener() and as soon as user clicks on it, code inside onClick(View v) executed. Button btnRestaurant = (Button) findViewById(R.id.btnRestaurant); btnRestaurant.setOnClickListener(new View.OnClickListener() { String Restaurant = "restaurant"; @Override public void onClick(View v) { Log.d("onClick", "Button is Clicked"); mMap.clear(); String url = getUrl(latitude, longitude, Restaurant); Object[] DataTransfer = new Object[2]; DataTransfer[0] = mMap; DataTransfer[1] = url; Log.d("onClick", url); GetNearbyPlacesData getNearbyPlacesData = new GetNearbyPlacesData(); getNearbyPlacesData.execute(DataTransfer); Toast.makeText(MapsActivity.this,"Nearby Restaurants", Toast.LENGTH_LONG).show(); 10 | P a g e } }); In the above code first of all Google Map is cleared using mMap.clear(); so that any pre-deposited markers are deleted. Then we are making a URL using getUrl() function. It will be used to get information about nearby restaurant on google maps. This URL is made according to Google Developer Guide for nearby places (https://developers.google.com/places/web-service/search). private String getUrl(double latitude, double longitude, String nearbyPlace) { StringBuilder googlePlacesUrl = new StringBuilder("https://maps.googleapis.com/maps/api/place/nearbysearch/json?"); googlePlacesUrl.append("location=" + latitude + "," + longitude); googlePlacesUrl.append("&radius=" + PROXIMITY_RADIUS); googlePlacesUrl.append("&type=" + nearbyPlace); googlePlacesUrl.append("&sensor=true"); googlePlacesUrl.append("&key=" + "AIzaSyATuUiZUkEc_UgHuqsBJa1oqaODI-3mLs0"); Log.d("getUrl", googlePlacesUrl.toString()); return (googlePlacesUrl.toString()); } Finally, markers are added on nearby restaurants using getNearbyPlacesData.execute(DataTransfer); Let’s see what is this getNearbyPlacesData is in Google Maps Search Nearby App. GetNearbyPlacesData Make a new class named GetNearbyPlacesData.java. This class should be extended from AsyncTask. import import import import import android.os.AsyncTask; android.util.Log; java.util.HashMap; java.util.List; org.json.JSONObject; import import import import import com.google.android.gms.maps.CameraUpdateFactory; com.google.android.gms.maps.GoogleMap; com.google.android.gms.maps.model.BitmapDescriptorFactory; com.google.android.gms.maps.model.LatLng; com.google.android.gms.maps.model.MarkerOptions; import com.google.android.gms.maps.GoogleMap; /** * Created by se245547 on 26/10/2016. */ public class GetNearbyPlacesData extends AsyncTask<Object, String, String> { String googlePlacesData; GoogleMap mMap; String url; 11 | P a g e @Override protected String doInBackground(Object... params) { try { Log.d("GetNearbyPlacesData", "doInBackground entered"); mMap = (GoogleMap) params[0]; url = (String) params[1]; DownloadUrl downloadUrl = new DownloadUrl(); googlePlacesData = downloadUrl.readUrl(url); Log.d("GooglePlacesReadTask", "doInBackground Exit"); } catch (Exception e) { Log.d("GooglePlacesReadTask", e.toString()); } return googlePlacesData; } @Override protected void onPostExecute(String result) { Log.d("GooglePlacesReadTask", "onPostExecute Entered"); List<HashMap<String, String>> nearbyPlacesList = null; DataParser dataParser = new DataParser(); nearbyPlacesList = dataParser.parse(result); ShowNearbyPlaces(nearbyPlacesList); Log.d("GooglePlacesReadTask", "onPostExecute Exit"); } private void ShowNearbyPlaces(List<HashMap<String, String>> nearbyPlacesList) { for (int i = 0; i < nearbyPlacesList.size(); i++) { Log.d("onPostExecute","Entered into showing locations"); MarkerOptions markerOptions = new MarkerOptions(); HashMap<String, String> googlePlace = nearbyPlacesList.get(i); double lat = Double.parseDouble(googlePlace.get("lat")); double lng = Double.parseDouble(googlePlace.get("lng")); String placeName = googlePlace.get("place_name"); String vicinity = googlePlace.get("vicinity"); LatLng latLng = new LatLng(lat, lng); markerOptions.position(latLng); markerOptions.title(placeName + " : " + vicinity); mMap.addMarker(markerOptions); markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RE D)); //move map camera mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng)); mMap.animateCamera(CameraUpdateFactory.zoomTo(11)); } } } In the above code DownloadUrl is a class which is used to retrieve data from URL using HttpURLConnection and File handling methods. We will discuss its code after this class. After retrieving data in the form of googlePlacesData we are passing it to onPostExecute method. dataParser.parse(result) is used to parse data and resultant is stored as a list in nearbyPlacesList. Now nearbyPlacesList will have all information about nearby restaurants which we can easily access and add markers on corresponding places. Markers are added in Google Maps using function ShowNearbyPlaces. This is pretty much self-explanatory. Data from URL will be in the form JSON which needs to be parsed, so we have made a class named DataParser. DataParser.java file should be added at the same path as MapsActivity.java. 12 | P a g e DataParser.java import android.util.Log; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** * Created by se245547 on 26/10/2016. */ public class DataParser { public List<HashMap<String, String>> parse(String jsonData) { JSONArray jsonArray = null; JSONObject jsonObject; try { Log.d("Places", "parse"); jsonObject = new JSONObject((String) jsonData); jsonArray = jsonObject.getJSONArray("results"); } catch (JSONException e) { Log.d("Places", "parse error"); e.printStackTrace(); } return getPlaces(jsonArray); } private List<HashMap<String, String>> getPlaces(JSONArray jsonArray) { int placesCount = jsonArray.length(); List<HashMap<String, String>> placesList = new ArrayList<>(); HashMap<String, String> placeMap = null; Log.d("Places", "getPlaces"); for (int i = 0; i < placesCount; i++) { try { placeMap = getPlace((JSONObject) jsonArray.get(i)); placesList.add(placeMap); Log.d("Places", "Adding places"); } catch (JSONException e) { Log.d("Places", "Error in Adding places"); e.printStackTrace(); } } return placesList; } private HashMap<String, String> getPlace(JSONObject googlePlaceJson) { HashMap<String, String> googlePlaceMap = new HashMap<String, String>(); String placeName = "-NA-"; String vicinity = "-NA-"; String latitude = ""; String longitude = ""; String reference = ""; Log.d("getPlace", "Entered"); try { if (!googlePlaceJson.isNull("name")) { placeName = googlePlaceJson.getString("name"); } if (!googlePlaceJson.isNull("vicinity")) { vicinity = googlePlaceJson.getString("vicinity"); } 13 | P a g e latitude = googlePlaceJson.getJSONObject("geometry").getJSONObject("location").getString("lat"); longitude = googlePlaceJson.getJSONObject("geometry").getJSONObject("location").getString("lng"); reference = googlePlaceJson.getString("reference"); googlePlaceMap.put("place_name", placeName); googlePlaceMap.put("vicinity", vicinity); googlePlaceMap.put("lat", latitude); googlePlaceMap.put("lng", longitude); googlePlaceMap.put("reference", reference); Log.d("getPlace", "Putting Places"); } catch (JSONException e) { Log.d("getPlace", "Error"); e.printStackTrace(); } return googlePlaceMap; } } DownloadUrl This class should be made at the same path as MapsActivity.java with the name DownloadUrl.java and add following code in it. import android.util.Log; import import import import import import java.io.BufferedReader; java.io.IOException; java.io.InputStream; java.io.InputStreamReader; java.net.HttpURLConnection; java.net.URL; /** * Created by se245547 on 26/10/2016. */ public class DownloadUrl { public String readUrl(String strUrl) throws IOException { String data = ""; InputStream iStream = null; HttpURLConnection urlConnection = null; try { URL url = new URL(strUrl); // Creating an http connection to communicate with url urlConnection = (HttpURLConnection) url.openConnection(); // Connecting to url urlConnection.connect(); // Reading data from url 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(); Log.d("downloadUrl", data.toString()); br.close(); 14 | P a g e } catch (Exception e) { Log.d("Exception", e.toString()); } finally { iStream.close(); urlConnection.disconnect(); } return data; } } Data returned from web will be in json format which user can get using HttpURLConnection. You can get more information about how to get data over web here. So this task will return JSON data returned from web. Our code of Google Maps Search Nearby App is complete for Adding markers at nearby restaurants on Google Maps. Now similarly you can do for School and Hospitals. The only change will be in getUrl() function where string will be passed as Hospital or School according to the button clicked. So finally our Google Maps Search Nearby App is complete. We would suggest you to turn on GPS and Internet Connection. Run this Google Maps Search Nearby Places App on any android device. It will first display your location. Now according to button clicked it will display nearby Restaurants, Schools or Hospitals as shown in following figure: 15 | P a g e