I'm working on a small app and have implemented Google Maps and Places api. Currently i'm able to see all my markers on the map and clustering working fine. I'm able to zoom in the clusters open up and able to see the markers. I have a spinner that has different types and once selected that type is passed to the places search string.
This is my maps code that includes the clustering:
public class MapsActivity extends FragmentActivity implements LocationListener,ClusterManager.OnClusterItemInfoWindowClickListener<MyItem> {
GoogleMap mMap;
double myLatitude = 0;
double myLongitude = 0;
HashMap<String, String> mMarker = new HashMap<String, String>();
PlaceJSONParser placeJsonParser = new PlaceJSONParser();
private ClusterManager<MyItem> mClusterManager;
protected MyItem clickedClusterItem;
String[] placeType;
String[] placeTypeName;
Spinner spinPlaceType;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mMap = mapFragment.getMap();
onMapReady();
// Array of place types
placeType = getResources().getStringArray(R.array.placeType);
// Array of place type names
placeTypeName = getResources().getStringArray(R.array.placeTypeName);
// Creating an array adapter with an array of Place types
// to populate the spinner
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, R.layout.spinner_item, R.id.textview, placeTypeName);
// Getting reference to the Spinner
spinPlaceType = (Spinner) findViewById(R.id.spinPlaceType);
// Setting adapter on Spinner to set place types
spinPlaceType.setAdapter(adapter);
spinPlaceType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
int selectedPosition = spinPlaceType.getSelectedItemPosition();
final String type = placeType[selectedPosition];
StringBuilder sb = new StringBuilder(
"https://maps.googleapis.com/maps/api/place/nearbysearch/json?");
sb.append("location=" + myLatitude + "," + myLongitude);
sb.append("&type=" + type);
sb.append("&radius=4000");
sb.append("&key=PLACES_KEY");
// Creating a new non-ui thread task to download Google place json
// data
PlacesTask placesTask = new PlacesTask();
// Invokes the "doInBackground()" method of the class PlaceTask
placesTask.execute(sb.toString());
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
StringBuilder sb = new StringBuilder(
"https://maps.googleapis.com/maps/api/place/nearbysearch/json?");
sb.append("location=" + myLatitude + "," + myLongitude);
sb.append("&type=restaurant");
sb.append("&radius=4000");
sb.append("&key=PLACES_KEY");
// Creating a new non-ui thread task to download Google place json
// data
PlacesTask placesTask = new PlacesTask();
// Invokes the "doInBackground()" method of the class PlaceTask
placesTask.execute(sb.toString());
}
});
// Will display next 20 places returned form the next_page_token
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab_more);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Finding you some more places.", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
StringBuilder sb = new StringBuilder(
"https://maps.googleapis.com/maps/api/place/nearbysearch/json?");
sb.append("pagetoken=" + placeJsonParser.getNext_Page_token());
sb.append("&key=PLACES_KEY");
// Creating a new non-ui thread task to download Google place json
// data
if (placeJsonParser.getNext_Page_token() == null || placeJsonParser.getNext_Page_token() == ""){
Snackbar.make(view, "No more places left to find.", Snackbar.LENGTH_SHORT)
.setAction("Action", null).show();
}
PlacesTask placesTask = new PlacesTask();
// Invokes the "doInBackground()" method of the class PlaceTask
placesTask.execute(sb.toString());
}
});
mMap.setOnInfoWindowClickListener(new OnInfoWindowClickListener() {
@Override
public void onInfoWindowClick(Marker marker) {
Intent detailsIntent = new Intent(getBaseContext(), PlaceDetailsActivity.class);
String reference = mMarker.get(marker.getId());
marker.getPosition();
detailsIntent.putExtra("reference", reference);
detailsIntent.putExtra("markerLat", myLatitude);
detailsIntent.putExtra("markerLong", myLongitude);
startActivity(detailsIntent);
}
});
}
public void onMapReady(){
// Enabling MyLocation in Google Map
mMap.setMyLocationEnabled(true);
mMap.getUiSettings().setCompassEnabled(true);
mMap.getUiSettings().setZoomControlsEnabled(true);
// Getting LocationManager object from System Service
// LOCATION_SERVICE
LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
// Creating a criteria object to retrieve provider
Criteria criteria = new Criteria();
// Getting the name of the best provider
String provider = locationManager.getBestProvider(criteria, true);
// Getting Current Location From GPS
Location location = locationManager.getLastKnownLocation(provider);
// onLocationChanged(location);
if (location != null) {
onLocationChanged(location);
}
}
/**
* A method to download json data from url
*/
private String downloadUrl(String strUrl) throws IOException {
String referer ="";
StringBuilder jsonResults = new StringBuilder();
HttpURLConnection conn = null;
try {
URL url = new URL(strUrl);
// Creating an http connection to communicate with url
conn = (HttpURLConnection) url.openConnection();
if (referer != null) {
conn.setRequestProperty("Referer", referer);
}
InputStreamReader in = new InputStreamReader(conn.getInputStream());
// Load the results into a StringBuilder
int read;
char[] buff = new char[1024];
while ((read = in.read(buff)) != -1) {
jsonResults.append(buff, 0, read);
}
// Displays the list of places found in the terminal.
Log.i("Data", "Places Found: " + jsonResults);
} catch (MalformedURLException e) {
Log.i("Google Places Utility", "Error processing Places API URL");
return null;
} catch (IOException e) {
Log.i("Google Places Utility", "Error connecting to Places API");
return null;
} finally {
if (conn != null) {
conn.disconnect();
}
}
return jsonResults.toString();
}
/**
* A class, to download Google Places
*/
private class PlacesTask extends AsyncTask<String, Integer, String> {
String data = null;
// Invoked by execute() method of this object
@Override
protected String doInBackground(String... url) {
try {
data = downloadUrl(url[0]);
} catch (Exception e) {
Log.d("Background Task", e.toString());
}
return data;
}
// Executed after the complete execution of doInBackground() method
@Override
protected void onPostExecute(String result) {
ParserTask parserTask = new ParserTask();
// Start parsing the Google places in JSON format
// Invokes the "doInBackground()" method of the class ParseTask
parserTask.execute(result);
}
}
/**
* A class to parse the Google Places in JSON format
*/
private class ParserTask extends
AsyncTask<String, Integer, List<HashMap<String, String>>> {
JSONObject jObject;
// Invoked by execute() method of this object
@Override
protected List<HashMap<String, String>> doInBackground(
String... jsonData) {
List<HashMap<String, String>> places = null;
try {
jObject = new JSONObject(jsonData[0]);
/** Getting the parsed data as a List construct */
places = placeJsonParser.parse(jObject);
} catch (Exception e) {
Log.d("Exception", e.toString());
}
return places;
}
// Executed after the complete execution of doInBackground() method
@Override
protected void onPostExecute(List<HashMap<String, String>> list) {
// Clears all the existing markers
mMap.clear();
setUpClusterer(list);
}
}
private void setUpClusterer(List<HashMap<String, String>> list) {
// Position the map.
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(myLatitude,myLongitude), 13));
// Initialize the manager with the context and the map.
// (Activity extends context, so we can pass 'this' in the constructor.)
mClusterManager = new ClusterManager<MyItem>(this, mMap);
// Point the map's listeners at the listeners implemented by the cluster
// manager.
mMap.setOnCameraChangeListener(mClusterManager);
mMap.setOnMarkerClickListener(mClusterManager);
mMap.setInfoWindowAdapter(mClusterManager.getMarkerManager());
mMap.setOnInfoWindowClickListener(mClusterManager);
mClusterManager.setOnClusterItemInfoWindowClickListener(this);
mClusterManager
.setOnClusterItemClickListener(new ClusterManager.OnClusterItemClickListener<MyItem>() {
@Override
public boolean onClusterItemClick(MyItem item) {
clickedClusterItem = item;
return false;
}
});
// Add cluster items (markers) to the cluster manager.
addItems(list);
mClusterManager.getMarkerCollection().setOnInfoWindowAdapter(
new MyCustomAdapterForItems());
}
public class MyCustomAdapterForItems implements GoogleMap.InfoWindowAdapter {
private final View myContentsView;
MyCustomAdapterForItems() {
myContentsView = getLayoutInflater().inflate(
R.layout.info_window, null);
}
@Override
public View getInfoWindow(Marker marker) {
TextView tvTitle = ((TextView) myContentsView
.findViewById(R.id.txtTitle));
TextView tvSnippet = ((TextView) myContentsView
.findViewById(R.id.txtSnippet));
tvTitle.setText(clickedClusterItem.getTitle());
tvSnippet.setText(clickedClusterItem.getSnippet());
return myContentsView;
}
@Override
public View getInfoContents(Marker marker) {
return null;
}
}
private void addItems(List<HashMap<String, String>> list) {
double latitude;
double longitude;
for (int i = 0; i < list.size(); i++) {
HashMap<String, String> hmPlace = list.get(i);
// Getting latitude of the place
latitude = Double.parseDouble(hmPlace.get("lat"));
// Getting longitude of the place
longitude = Double.parseDouble(hmPlace.get("lng"));
String name = hmPlace.get("place_name");
// Getting vicinity
String vicinity = hmPlace.get("vicinity");
MyItem offsetItem = new MyItem(latitude, longitude, hmPlace.get("reference"), name, vicinity);
mClusterManager.addItem(offsetItem);
}
}
public void onClusterItemInfoWindowClick(MyItem item) {
Intent placesIntent = new Intent(getBaseContext(), PlaceDetailsActivity.class);
String reference = item.getReference();
placesIntent.putExtra("name", item.getTitle());
placesIntent.putExtra("reference", reference);
placesIntent.putExtra("sourcelat", myLatitude);
placesIntent.putExtra("sourcelng", myLongitude);
startActivity(placesIntent);
}
@Override
public void onLocationChanged(Location location) {
myLatitude = location.getLatitude();
myLongitude = location.getLongitude();
LatLng myLocation = new LatLng(myLatitude, myLongitude);
mMap.moveCamera(CameraUpdateFactory.newLatLng(myLocation));
mMap.animateCamera(CameraUpdateFactory.zoomTo(13));
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
}
my myItem class to get info for the markers:
package com.example.tariq.outandabout;
import com.google.android.gms.maps.model.LatLng;
import com.google.maps.android.clustering.ClusterItem;
public class MyItem implements ClusterItem {
LatLng mPosition;
private String reference,placeTitle,snippet;
public MyItem(double lat, double lng,String val,String title, String snip) {
mPosition = new LatLng(lat, lng);
reference=val;
placeTitle=title;
snippet = snip;
}
@Override
public LatLng getPosition() {
// TODO Auto-generated method stub
return mPosition;
}
public String getReference() {
// TODO Auto-generated method stub
return reference;
}
public String getTitle() {
// TODO Auto-generated method stub
return placeTitle;
}
public String getSnippet() {
// TODO Auto-generated method stub
return snippet;
}
}
Currently only red markers are getting displayed but I was wondering if there is a way to have a different markers depending on the type selected from the spinner, For example if I select Hospital then the markers are shown as little hospital icons, if I select ATM, then a little ATM icon appears.
Any help will be appreciated.
Firstly you have to store all the info you need (at this situation just LatLng and marker icon) insite ClusterItem object.
public class MarkerItem implements ClusterItem {
private String title;
private String snippet;
private LatLng latLng;
private BitmapDescriptor icon;
public MarkerItem(MarkerOptions markerOptions) {
this.latLng = markerOptions.getPosition();
this.title = markerOptions.getTitle();
this.snippet = markerOptions.getSnippet();
this.icon = markerOptions.getIcon();
}
@Override
public LatLng getPosition() {
return latLng;
}
public String getTitle() {
return title;
}
public String getSnippet() {
return snippet;
}
public void setLatLng(LatLng latLng) {
this.latLng = latLng;
}
public BitmapDescriptor getIcon() {
return icon;
}
public void setIcon(BitmapDescriptor icon) {
this.icon = icon;
}
}
The next step would be to make cluster renderer show your icon instead of default maker icon. To achieve that, you need to extend DefaultClusterRenderer object:
public class ClusterRenderer extends DefaultClusterRenderer<MarkerItem> {
public ClusterRenderer(Context context, GoogleMap map, ClusterManager<MarkerItem> clusterManager) {
super(context, map, clusterManager);
clusterManager.setRenderer(this);
}
@Override
protected void onBeforeClusterItemRendered(MarkerItem markerItem, MarkerOptions markerOptions) {
if (markerItem.getIcon() != null) {
markerOptions.icon(markerItem.getIcon()); //Here you retrieve BitmapDescriptor from ClusterItem and set it as marker icon
}
markerOptions.visible(true);
}
}
Finally, you have to initialize the clusterRenderer and markerItems
ClusterManager clusterManager = new ClusterManager<>(context, googleMap);
ClusterRenderer clusterRenderer = new ClusterRenderer<>(activity, googleMap, clusterManager); // not needed to use clusterManager.setRenderer method since i made it in constructor
MarkerOptions markerOptions = new MarkerOptions()
.position(new LatLng(latitude, longitude))
.icon(BitmapDescriptorFactory.fromResource(R.drawable.your_resource_icon));
MarkerItem markerItem = new MarkerItem(markerOptions);
clusterManager.addItem(markerItem);
You can implement your own logic, which icon you want to pass to markerItem here.
EDIT
To pass different icons, you could create a separate method for that
Example:
public MarkerOptions getMarkerOptions(LatLng latLng, String title, String snippet, int iconRes) {
return new MarkerOptions()
.title(title)
.snippet(snippet)
.position(latLng)
.icon(BitmapDescriptorFactory.fromResource(iconRes));
}
EDIT 2 I updated MarkerItem class to suit your needs, replace your MyItem class with MarkerItem class. Add your items using this class and update it to suit your needs
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With