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
}
});
Related
I have a list of places to visit with lat and lng coords, so when a person click on one of the location pin in map it should scroll to that card .
Assuming you data models are the same (data model for markers = data model for recyclerView items) or at least they share the same latitude and longitude.
What you should do on onMarkerClick(Marker marker) is something like this:
public boolean onMarkerClick(Marker marker) {
for (DataModel model : recyclerViewAdapter.getDataModels()){
LatLng latLng = marker.getPosition();
// now everything is speculation from this point on
// You need to check if latLng equals the latLng of the model
// and then you use recyclerview.scrollToPosition(position) where position is the position in the recyclerViewAdapter
// and finally don't forget to break the loop
}
return true;
}
Hope that helps.
I trying to set marker visible on the map when I am in range and set invisible when i am not in range. When am moving and enter area marker appear - but when I get out of range marker is still visible. Here is my code onLocationUpdate.
I iterate over my database and adding markers. getDeviceLocation return Ltglng with my current location. I implement this also for GPS provider. Any ideas will be helpfull ty!
locManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 1, new LocationListener() {
#Override
public void onLocationChanged(Location location) {
Cursor res = activityA.myDB.getAllData();
while (res.moveToNext()) {
double ltd = Double.valueOf(res.getString(3));
double lng = Double.valueOf(res.getString(4));
LatLng hole = new LatLng(ltd, lng);
Marker marker = mMap.addMarker(new MarkerOptions().position(hole)
.title(res.getString(1)).visible(false));
if (SphericalUtil.computeDistanceBetween(getDeviceLocation(), marker.getPosition()) <3 ) {
marker.setVisible(true);
}
}
}
From what you have provided this is what I can gather.
You are adding the marker (originally set to invisible), and then if it meets your if statement, you make them invisible. The problem is, I don't see any place, where you would make them invisible again, or remove them.
Do you save these markers in your activity? For example in an ArrayList?
I have two suggestions:
1)Either call mMap.clear() before before your while statement. This will clear the map of any markers, and then adds the new ones as they are created.
2)Save all your markers in an ArrayList and then in your onLocationChanged, use a for loop to go through all your markers and make the ones out of range invisible. Here is an example:
for (Marker marker: mMarkerArrayList) {
if (outOfRange()) {
marker.visible(false);
}
}
Here mMarkerArrayList is the ArrayList containing all your markers. outOfRange() is a helper function that returns a boolean if the marker is outOfRange.
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 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.
Hi i've the following code:
PolylineOptions myPolyline =new PolylineOptions();
go.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
...
myMap.addMarker(new MarkerOptions().position(start));
myPolyline.add(start);
myMap.addMarker(new MarkerOptions().position(destination));
myPolyline.add(destination);
fixZoom();
...
}
private void fixZoom() {
List<LatLng> points = myPolyline.getPoints();
LatLngBounds.Builder bc = new LatLngBounds.Builder();
for (LatLng item : points) {
bc.include(item);
}
myMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bc.build(), 50));}
If i've one of my markers OUT of my current screen, this code work fine but, when i've already all my markers in the current camera screen, zoom level wont change while I would like to see it changed to the minimum possible (zoom IN so).
I hope I made myself clear.
If you want to set zoom to a latlngbound, you have to set a zize to the map.
Look this example!
How to show multiple markers on MapFragment in Google Map API v2?