I need display >100 markers on the map and show info window after click on marker.
I have used android google maps api v2, but it is lagging with such number of markers. So I decided switch to android-maps-extensions. After I did it info window has stopped appear after click on marker.
Here is my code:
private void prepareMap() {
SupportMapFragment fragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.locationsMap);
map = fragment.getExtendedMap();
ClusteringSettings settings = new ClusteringSettings();
settings.clusterOptionsProvider(new ClusterOptionsProvider() {
#Override
public ClusterOptions getClusterOptions(List<Marker> markers) {
float hue = BitmapDescriptorFactory.HUE_RED;
BitmapDescriptor icon = BitmapDescriptorFactory.defaultMarker(hue);
return new ClusterOptions().icon(icon);
}
});
settings.clusterSize(100);
settings.addMarkersDynamically(true);
map.setClustering(settings);
map.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
// breakpoint here are working. marker is not null
marker.showInfoWindow();
return false;
}
});
}
public void onLoadFinished(android.support.v4.content.Loader loader, Cursor cursor) {
if (map != null) {
cursor.moveToFirst();
int count = cursor.getCount();
for (int i = 0; i < count; i++) {
Location location = new Location(cursor);
MarkerOptions options = new MarkerOptions();
options.position( new LatLng(location.getLatitude(), location.getLongitude()));
options.title(location.getTitle()); // title is not null
options.snippet(location.getAddress()); // address is not null
options.data(location);
map.addMarker(options);
cursor.moveToNext();
}
}
}
Any suggestions? I have try use own InfoWindowAdapter, but it is not scaling this window for appropriate content, however I am using wrap_content attribute in xml.
As there is no support for setting title on ClusterOptions (yet), you will have to use InfoWindowAdapter (you may put a separate question on why it is not working as you want it) or add title to ClusterOptions yourself and make sure its value is forwarded to virtual marker that represents cluster. This sounds complicated but is fairly easy. You may also want to add issue on GitHub regarding title not being supported for clustrers.
After that you may use something like:
return new ClusterOptions().icon(icon).title("Test");
Then you will have to figure out what title will best fit your needs. You will most likely want to use List<Marker> to calculate title for this group of markers.
Note: If you zoom enough to show separate markers, you can click on them to show title. If not, there is something more wrong in your code.
Related
Here is the problem:
I want to show the user the best route to reach a marker after the user presses a button inside the infoWindow. The problem is, I can't get the marker's Location data due to some problem with Latlng and Position classes. (I am using the Mapbox example to get the route, so I need two Position values)
So basically, I need to update the variable Destination with the marker's position by clicking a button inside the custom infowindow. Yet I have no idea how can I do that even though going through a lot on searching Google and Stack Overflow. Can someone help me? (Cammack!) Thanks a bunch for the help!
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
// Origem: Rodoviaria
final Position origin = Position.fromCoordinates(-47.882645, -15.794082);
// Destino: Reitoria
final Position destination = Position.fromCoordinates(-47.866611, -15.762604);
//...
mapboxMap.setInfoWindowAdapter(new MapboxMap.InfoWindowAdapter() {
#Nullable
#Override
public View getInfoWindow(#NonNull Marker marker) {
//...
final Marker marcador = marker;
botaoIr = (Button)v.findViewById(R.id.botaoIr);
botaoIr.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//I have been trying the method below, but I am having trouble with LatLng and Position classes!
//LatLng ponto = marcador.getPosition();
//destination = new Position(ponto.getLongitude(),ponto.getLatitude());
try {
getRoute(origin, destination);
} catch (ServicesException servicesException) {
servicesException.printStackTrace();
}
}
});
}
});
//...
}
To create a position call fromCoordinates().
destination = Position.fromCoordinates(ponto.getLongitude(),ponto.getLatitude());
If you want to remove all annotations on your map, you can call "removeAnnotations()" on your MapBoxMap-Instance:
mapboxMap.removeAnnotations();
..or you can call "remove()" on any annotation from MapBox itself (Polygon, Polyline or Marker)
Polygon pol = new Polygon();
pol.remove();
If it's still existing and you just want to change & update an annotation, call "updatePolygon()", "updateMarker" or "updatePolyline() like this:
mapboxMap.updatePolygon(myPolgonInstance);
mapboxMap.updateMarker(myMarkerInstance);
...
I haven't found any complete explanation on how to use this technique on Android so I decided to create a Q&A thread.
If your app has to show a large amount of markers on a google map and clustering them is not enough to prevent your app from working too slow, then one of your best choices is to use this Viewport Marker Management technique. You can read the theoretical explanation here: https://developers.google.com/maps/articles/toomanymarkers
I wrote a short guide below...
1°--- In the activity where map is created you have to set the OnCameraChangeListener and get the bounds of your screen like this:
mMap.setOnCameraChangeListener(new OnCameraChangeListener() {
#Override
public void onCameraChange(CameraPosition arg0) {
LatLngBounds bounds = mapa.getProjection().getVisibleRegion().latLngBounds;
}
2°--- This step may vary depending how you fetch the markers data. Basically, what you have to do is to calculate if the lat and long of each of your markers are inside the screen bounds. I will show you how to do it by fetching the data from a SQLite data base storing latitud and longitude in two different DOUBLE clomuns inside the markers table.
mMap.setOnCameraChangeListener(new OnCameraChangeListener() {
#Override
public void onCameraChange(CameraPosition arg0) {
LatLngBounds bounds = mMap.getProjection().getVisibleRegion().latLngBounds;
LatLng northeast = bounds.northeast;
String boundLat = String.valueOf(northeast.latitude);
String boundLong = String.valueOf(northeast.longitude);
LatLng southwest = bounds.southwest;
String boundLat2 = String.valueOf(southwest.latitude);
String boundLong2 = String.valueOf(southwest.longitude);
//Remove all markers from map
mMap.clear(); // or if your a using cluster manager:
//mClusterManager.clearItems();
String[] fields = new String[] { "name", "latitude", "longitude" };
String[] args = new String[] {boundLat, boundLong, boundLat2, boundLong2,};
Cursor markers = dataBase.query("markers", fields, "latitude<=? AND longitude<=? AND latitude>=? AND longitude>=?");
if (markers.moveToFirst()) {
do {
mMap.addMarker(new MarkerOptions()
.position(new LatLng(marker.getDouble(1), marker.getDouble(2)))
.title(marker.getString(0)) );
// or if you are using cluster manager create and add the items as you normaly do.
} while (c.moveToNext());
//if using cluster manager add :
//mClusterManager.cluster();
}
}
});
The idea is pretty easy, just have in mind that your markers lat and longi have to be smaller than the northeast position of your screen and bigger than the southwest corner, or just use the LatLngBounds.contains function.
EDITED:
To avoid InfoWindow getting closed when clicking on a marker which is not already in the center of the screen, you can change the marker click listener default action, removing the camera move.
map.setOnMarkerClickListener(new OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker arg0) {
arg0.showInfoWindow();
return true; //must be true, if not, it will execute the default code after yours
}
});
I'm trying to create an android widget based on a ViewSwitcher with a MapView and StreetViewPanoramaView inside. Users can switch from street to map view with a overlayed control bar.
So far all is working fine for the map part, but not for the street view part.
On first display it seems to work but when i tried to move inside (pan and/or zoom) StreetViewPanorama crash (become black).
this is how i create my StreetViewPanorama :
final StreetViewPanoramaOptions options = new StreetViewPanoramaOptions();
options.streetNamesEnabled(true)
.zoomGesturesEnabled(true)
.panningGesturesEnabled(true);
mStreetView = new StreetViewPanoramaView(getActivity(), options);
mStreetView.onCreate(savedInstanceState);
And how I defiened the location for both (map and street view)
// Set map to position and add a new marker
final GoogleMap map = mMapView.getMap();
map.moveCamera(CameraUpdateFactory.newLatLngZoom(mLocation, DEFAULT_ZOOM_LEVEL));
if (mMarker != null){
mMarker.remove();
}
mMarker = map.addMarker(new MarkerOptions().draggable(false).position(mLocation));
// Set street view to location.
final StreetViewPanorama street = mStreetView.getStreetViewPanorama();
street.setPosition(mLocation, 50);
// Hide by default controls and street view button.
// I will be enabled only if it's a valid street view location
mControlsView.setVisibility(View.GONE);
mStreetBtnContainer.setVisibility(View.GONE);
switchToMapView();
// we must add a delay to check if the new street view location is valid.
// see : http://stackoverflow.com/a/23785186
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mHasStreetView = street.getLocation() != null;
if (mHasStreetView){
mStreetBtnContainer.setVisibility(View.VISIBLE);
}
showControlView();
}
}, STREET_DELAY_MILLIS);
Did someone encounter the same problem ?
Cheers.
I have an ArrayList of a custom class. There are about 10 objects in the list, each with details like Title, Snippet, LatLng. I have successfully added all 10 to a Map by using my custom class functions like getTitle, getSnippet, getLatLng etc.
Now, when I click the info window (of the marker), I want to be able to somehow KNOW which object of my custom class does that marker correspond to.
For example, if I click the McDonald's market, I want to be able to know which item from my ArrayList did that marker belong to.
I've been looking at the MarkerOptions and there doesn't seem to be anything in there that I can use to identify the relevant custom object with.
If the question is too confusing, let me make it simple:
ArrayList<CustomObj> objects = blah
map.addMarker(new MarkerOptions().position(new LatLng(
Double.parseDouble(result.get(i).getCompanyLatLng()
.split(",")[0]), Double.parseDouble(result
.get(i).getCompanyLatLng().split(",")[1])))
.title(result.get(i).getCompanyName())
.snippet(result.get(i).getCompanyType())
.icon(BitmapDescriptorFactory
.fromResource(R.drawable.pin)));
Now when this is clicked, I go on to the next page. The next page needs to know WHICH object was clicked so that I can send the other details to that page (e.g. Image URLs that need to be loaded etc).
How do I add a unique integer or any identifier to my marker?
One correct way is to use Map<Marker, CustomObj> which stores all markers:
Marker marker = map.addMarker(...);
map.put(marker, result.get(i));
and in onInfoWindowClick:
CustomObj obj = map.get(marker);
Another try is using Android Maps Extensions, which add getData and setData method to Marker, so you can assign your CustomObj object after creating marker and retrieve it in any callback.
I used the the snippet text for saving an unique ID. If you need the snippet it's will be for the popup and there you can just make your own (by finding the object from the ID) so you wont miss it but you'll certainly miss a unique ID for identifying the objects.
To find the right object I just iterate through them:
for(SomeObject anObject : someObjects) {
if(marker.getSnippet().equalsIgnoreCase(anObject.getID())) {
//you got at match
}
}
According to the discussion at the following link, Marker.equals and Marker.hashCode don't work sometimes. For example, when the map is panned or zoomed, or it's restarted/resumed.
Add Tag / Identifier to Markers
So Marker's ID is a better key than Marker itself for a HashMap.
Map<String, MyObject> markerMap = new HashMap<String, MyObject>();
Marker marker = map.addMarker(...);
MyObject myObject = new MyObject();
markerMap.put(marker.getId(), myObject);
I think using latitude and longitude, this could be done
map.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(final Marker marker) {
LatLng pos = marker.getPosition();
int arryListPosition = getArrayListPosition(pos);
return true;
}
});
and the method getArrayListPosition is
private int getArrayListPosition(LatLng pos) {
for (int i = 0; i < result.size(); i++) {
if (pos.latitude == result.get(i).getCompanyLatLng().split(",")[0]) {
if (pos.longitude == result.get(i).getCompanyLatLng()
.split(",")[1])
return i;
}
}
return 0;
}
This method will return you the position of your ArrayList who's Marker you have clicked. and you can get data from that position.
Hope it helps...
I am 4 years late to answer but I really amazed why no-one talked about marker.setTag and marker.getTag method.
As written in google API docs,
This is easier than storing a separate Map<Marker, Object>
They have introduce setTag and getTag to avoid use of Map<...>. You can use it as following way.
Marker marker = map.addMarker(...);
marker.setTag(result.get(i)); //Store your marker information here.
//To fetch your object...
map.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
CustomObj obj = (CustomObj)marker.getTag();
//Your click handle event
// Pass the obj to another activity and you are done
return false;
}
});
I would like (in my Android app using Google maps api v2) to hide or show markers on my GoogleMap object according to a category, just like in the google maps web api, for example:
I have a GoogleMap with 50 Markers, 20 of them represent restaurants, 20 them represent bus stops, and 10 are cinemas.
Is it possible on Android google maps api v2 to do filtering on these markers, by hiding all the restaurant markers if we un-tick a checkbox for example?
I would like to do something like that but on my Android device using google maps api v2: http://www.geocodezip.com/v3_MW_example_categories.html
Sorry for the basic question but I am a beginner.
Try this way.
Marker restuarantMarkers = gMap.addMarker(new MarkerOptions()
.position(latlng)
.title("MyPlace").icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_pin)).draggable(true));
On Click Event
restuarantMarkers.setVisible(false);
This way can do using loop..
Let me know if it works for you.
You can use dialog if you want to filter your locations.
final Dialog dialog = new Dialog(context);
dialog.setContentView(R.layout.dialog);
Button bt_ok = (Button) dialog.findViewById(R.id.button1);
final CheckBox cb1 = (CheckBox) dialog.findViewById(R.id.checkBox1);
final CheckBox cb2 = (CheckBox) dialog.findViewById(R.id.checkBox2);
dialog.setTitle("Category");
bt_ok.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
mMap.clear();
if (cb1.isChecked()){
mMap.addMarker(new MarkerOptions().position(new LatLng(44.109798, 15.242270)).title("Pin").icon(BitmapDescriptorFactory.fromResource(R.drawable.museum)));
}
if (cb2.isChecked()){
mMap.addMarker(new MarkerOptions().position(new LatLng(44.209798, 15.392270)).title("Pin 2").icon(BitmapDescriptorFactory.fromResource(R.drawable.restaurants)));
}
dialog.dismiss();
}
});
dialog.show();
Yes you can! And here's how...
//mMap is an instance of GoogleMap that has already been initialized else where
mMap.setOnCameraChangeListener(getCameraChangeListener());
getCameraChangeListener()
public OnCameraChangeListener getCameraChangeListener()
{
return new OnCameraChangeListener()
{
#Override
public void onCameraChange(CameraPosition position)
{
addItemsToMap(this.items);
}
};
}
//Your "Item" class will need at least a unique id, latitude and longitude.
private void addItemsToMap(List<Item> items)
{
if(this.mMap != null)
{
LatLngBounds bounds = this.mMap.getProjection().getVisibleRegion().latLngBounds;
for(Item item : items)
{
if(bounds.contains(new LatLng(item.getLatitude(), item.getLongitude()))
{
if(!courseMarkers.containsKey(item.getId()))
{
this.courseMarkers.put(item.getId(), this.mMap.addMarker(getMarkerForItem(item)));
}
}
else
{
if(courseMarkers.containsKey(item.getId()))
{
courseMarkers.get(item.getId()).remove();
courseMarkers.remove(item.getId());
}
}
}
}
}
Here, LocationArray is ArrayList of Locations, which are plotted on map. We want to show all markers plotted on map.
private void FitAllMarkers() {
LatLngBounds.Builder builder = new LatLngBounds.Builder();
for (int i = 0; i < LocationArray.size(); i++) {
builder.include(new LatLng(Double.parseDouble(LocationArray.get(i).getLat()), Double.parseDouble(LocationArray.get(i).getLng())));
}
LatLngBounds bounds = builder.build();
mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100));
}