Google Maps V3 2017 still slow when adding markers/circles - android

Google Maps API on Android still has an issue where it takes a long time to load many markers/circles. In my case I am drawing 130+ circles with Lat/Lng to the Google Map. This process takes 20s to complete. Here is the code for adding the circles which is called after #Override public void onMapLoaded:
List<Crime> crimes = mCrimeManager.getListOfCrimes();
Log.d("CRIME SIZE TAG", " " + crimes.size());
for(Crime crime:crimes)
{
if(!(getLocationOfCrime(crime) == null))
{
Circle circle = googleMap.addCircle(new CircleOptions()
.clickable(true)
.radius(5)
.center(getLocationOfCrime(crime)));
circle.setTag(crime);
I know that this is the issue because removing this code causes everything to run smoothly with no delay.
I have looked at multiple questions in the past but most are using clustering or loading only with a certain range which can not be applied to my case. I have tried running this on AsyncTask with a runOnUiThread(); in doInBackground(); but that still yields same performance.

Okay guys here is the solution. It turns out adding the markers is not processor intensive. Geolocating is the issue. If you are running into this issue it is because you are geolocating on the main thread. The way I fixed my problem was putting geolocation calculations on AsyncTask (doInBackground();) then on onPostExecute adding the markers.
Still there is an issue where geolocation takes time(UI running smoothly but no markers), but this is a Google Maps API issue. I sorta fixed this by doing the geolocation once at startup then storing everything into SQLiteDataBase then reading in from there after each time.
Thanks

Related

Android - google maps gets stuck

I'm developing a App which display a Google map and a bunch of markers on it. There's a lot of markers so I divided them in smaller groups and display only those, which are in some bounds depending on the current position of the camera.
To do that I'm using the GoogleMap.OnCameraIdleListener. First I remove the listener, do my calculations and drawing and then I restore the listener to the Fragment containing my map:
#Override
public void onCameraIdle() {
mMap.setOnCameraIdleListener(null);
clearMap();
findTheMarkersInBounds();
displayTheMarkers();
mMap.setOnCameraIdleListener(this);
}
This way I only draw the markers I need to display and the performance is way better then having 1000 markers on the map at once. I also draw about the same number of polylines but that's not the point now.
For some strange reasons, after some panning and zooming the maps doesn't respond anymore. Can't zoom it nor pan it. App displays a dialog that it is not responding and I should wait or close the app. No erros are displayed in logcat. I can't exactly tell when this happens. Sometimes after the first pan, sometimes I can move around 2-3 minutes. Same thing happens on the emulator and on the physical device.
Anyone experienced something like this? Thanks!
Or am I approaching this the wrong way? How else should I optimize the map to display about 1000 markers and polylines. (The markers have text on them, so it can't be the same Bitmap and all of the polylines can have different colors and need to be clickable, so I can't combine them into one large polyline)
EDIT. A little more info about my methods:
After all the marker positions are loaded from the internal database, I do a for-loop through all of them and based on their position and I place them to the corresponding region. Its an 2D array of lists.
My whole area is divided to 32x32 smaller rectangular areas. When I'm searching for the markers to display, I determine which region is in view and display only those markers, which are in this area.
This way I don't need to loop over all of the markers.
My methods (very simplified) look like this:
ArrayList<MarkerObject> markersToDisplay = new ArrayList<MarkerObject>();
private void findTheMarkersInBounds() {
markersToDisplay.clear();
LatLngBounds bounds = mMap.getProjection().getVisibleRegion().latLngBounds;
int[] regionCoordinates = getRegionCoordinates(bounds); // i, j coordinates of my regions [0..31][0..31]
markersToDisplay.addAll(subdividedMarkers[regionCoordinates[0]][regionCoordinates[1]]);
}
private void drawMarkers() {
if ((markersToDisplay != null) && (markersToDisplay.size() > 0)) {
for (int i=0; i<markersToDisplay.size(); i++) {
MarkerObject mo = markersToDisplay.get(i);
LatLng position = new LatLng(mo.gpsLat, mo.gpsLon);
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(createMarker(getContext(), mo.title));
GroundOverlay m = mMap.addGroundOverlay(groundOverlayOptions.image(bitmapDescriptor).position(position, 75));
m.setClickable(true);
}
}
}
It is hard to help you without source code of findTheMarkersInBounds() and displayTheMarkers(), but seems, you need different approach to increase performance, for example:
improve your findTheMarkersInBounds() logic if it possible;
runfindTheMarkersInBounds() in separate thread and show not all markers in same time, but one by one (or bunch of 10..20 at one time) during findTheMarkersInBounds() searching;
improve your displayTheMarkers() if it possible, actually may be use custom drawing on canvas (like in this answer) instead of creating thousands Marker objects.
For question updates:
Small improvements (first, because they are used for main):
pass approximately max size of markersToDisplay as constructor parameter:
ArrayList<MarkerObject> markersToDisplay = new ArrayList<MarkerObject>(1000);
Instead for (int i=0; i<markersToDisplay.size(); i++) {
use for (MarkerObject mo: markersToDisplay) {
Do not create LatLng position every time, create it once and store in MarkerObject fields.
Main improvement:
This lines are the source of issues:
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(createMarker(getContext(), mo.title));
GroundOverlay m = mMap.addGroundOverlay(groundOverlayOptions.image(bitmapDescriptor).position(position, 75));
IMHO using Ground Overlays for thousands of markers showing is bad idea. Ground Overlay is for several "user" maps showing over default Google Map (like local plan of Park or Zoo details). Use custom drawing on canvas like on link above. But if you decide to use Ground Overlays - do not recreate them every time: create it once, store references to them in MarkerObject and reuse:
// once when marker created (just example)
mo.overlayOptions = new GroundOverlayOptions()
.image(BitmapDescriptorFactory.fromBitmap(createMarker(getContext(), mo.title)))
.position(mo.position, 75))
.setClickable(true);
...
// in your drawMarkers() - just add:
...
for (MarkerObject mo: markersToDisplay) {
if (mo.overlayOptions == null) {
mo.overlayOptions = createOverlayOptionsForThisMarker();
}
mMap.addGroundOverlay(mo.overlayOptions)
}
But IMHO - get rid of thousands of Ground Overlays at all - use custom drawing on canvas.
After further investigation and communication with the google maps android tech support we came to a solution. There's a bug in the GroundOverlay.setZIndex() method.
All you have to do is to update to the newest API version. The bug is not present anymore in Google Maps SDK v3.1.
At this moment it is in Beta, but the migration is pretty straightforward.

How to deal with Large number of ClusterItems in Cluster - Android GoogleMaps V2

I am using GoogleMapsV2. I have around 10 000 cluster items which I bulk add to ClusterManager (ClusterManager.addItems(Collection).
My problem is that the clustering and de-clusterin process is lagging and slow. I believe it shouldn't be that slow with around 10 000 items. Testing on OnePlus3 (with 6GB or ram) so it's not the phone.
Here is cluster relate code in activity:
// Rent markers
rentMarkerList = new ArrayList<>();
rentClusterManager = new ClusterManager<OffersMarker>(this, gmap);
rentClusterRenderer = new OffersClusterRenderer(this, gmap, rentClusterManager);
rentClusterManager.setRenderer(rentClusterRenderer);
for (AdMarker adMarker : adsArray) { //<-- adsArray.size() = 6500
OffersMarker offsetItem = makeOffersMarker(adLocation, nrOfAds, realEstateAdIds, OffersMarker.DEALTYPE.SALE_CODE);
rentMarkerList.add(offsetItem);
}
// Do bulk add of Markers into ClusterManager.
rentClusterManager.addItems(saleMarkerList);
rentClusterManager.cluster();
I went over my ClusterRenderer and I am not performing any large operations in onBeforeClusterItemRendered() and onBeforeClusterRendered().
It seems that the application is much slower when doing Clustering than it is in de-clustering but that may also be because clustering is done usually on a much smaller zoom level and thus more markers are displayed.
My android monitor shows that the clustering process alters the memory and CPU and not the GPU so it's not a graphics problem.
Solutions I tried:
Doing dynamic cluster rendering like described here. In this case you loop through your markers list and see which are in the Viewport bounds so you only display those markers. This made the whole thing worse because I had to loop through all the 10 000 markers every time onCameraIdle... was called.
Any ideas where to start optimizing this problem?
Do you use rentMarkerList anywhere else? If not, you can refactor your code in this way:
rentClusterManager = new ClusterManager<OffersMarker>(this, gmap);
rentClusterRenderer = new OffersClusterRenderer(this, gmap, rentClusterManager);
rentClusterManager.setRenderer(rentClusterRenderer);
for (AdMarker adMarker : adsArray) { //<-- adsArray.size() = 6500
rentClusterManager.addItem(makeOffersMarker(adLocation, nrOfAds, realEstateAdIds, OffersMarker.DEALTYPE.SALE_CODE));
}
rentClusterManager.cluster();
So you don't create an extra list of markers and don't add them all at the same time, but add them one by one.
You can also repleace foreach loop with simple for loop. According to this Android performance patterns episode, it works faster.
I don't think that that will significantly improve the performance, it's just little optimization tips, that may help.
After straggling with 30K markers on ios i just dropped google cluster manager, it has very poor preformance and not scalable at all. there plenty other free cluster libs, just drop google-utiils.
I have swapped it for ClusterKit - works grate!
https://github.com/hulab/ClusterKit - (iOS)
There is implementation of a view-based clustering algorithm in android-maps-utils library that can be used for better performance of clustering.
I had 5000+ markers and clustering was struggling when zooming in and out. After setting Algorithm to NonHierarchicalViewBasedAlgorithm, clustering is very smooth and faster.
mClusterManager = new ClusterManager<>(this.mContext, mGoogleMap);
DisplayMetrics metrics = new DisplayMetrics();
this.mActivity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
mClusterManager.setAlgorithm(new NonHierarchicalViewBasedAlgorithm<MyItem>(metrics.widthPixels, metrics.heightPixels));

How to move/animate a marker smoothly along a poly-line points on google maps?

I am a newbie in android development and currently working on an app where i need to show a marker moving along a path(poly-line). I have googled a lot for the functionality and got various answers too but for now i have selected the answer from this
question which better suits the requirements. But the problem with the solution is the marker moving very fast. Please suggest me any correction or other solution that will slow down the speed of animation of moving marker or something like that.
Thanks
From this answer in related SO post, you need to update the marker more than every 1/10 fraction of the polyline (at least every few pixels). Call the update method more frequently and don't delete and re-add the marker.
Sample code:
var counter = 0;
interval = window.setInterval(function() {
counter++;
// just pretend you were doing a real calculation of
// new position along the complex path
var pos = new google.maps.LatLng(35, -110 + counter / 100);
marker.setPosition(pos);
if (counter >= 1000) {
window.clearInterval(interval);
}
}, 10);
Check these related SO threads which might help:
move marker on google maps api 2
Google map: moving marker and map together smoothly along with user?
Hope this helps!

Replicating Maps Follow Current Location Feature in Android

I am looking to figure out how I can replicate the follow current location feature that exists in Maps on Android (and iPhone) using the Google Maps external library. The way the feature works in Maps is that when you press the button it begins panning with your location until you touch (and/or move) the map. Using the following I have been able to get my app to keep the current location on the screen but not keep the current location in the center of the screen (it pans when the location gets near the edge of the screen whereas Maps keeps it in the center the whole time):
butMapCurrentLocation.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
runOnUiThread(new Runnable() {
public void run() {
mapController.animateTo(myLocationOverlay.getMyLocation());
}
});
}
});
I also tried setCenter rather than animateTo and they seem to have the same effect.
Additionally it is worth noting I need to be able to stop this function that keeps the location in the center (such as when another button is pressed or the map is touched). The Runnable above doesn't stop right away and I cannot get it to stop properly when requested even when using .cancel as discussed here: Android: How do I stop Runnable?
There is no official way yet to do that in Google Maps v2.
You can do what you want however. There is two things you have to do:
Register for location updates from some LocationProviders (eg gps) to get the user's location. When you get the location animate map to the new location.
Stop this when the user moved the map.
So doing 1. is easy, doing 2. is not possible officially with the Maps v2 api. Look at this issue for more details.
The workaround suggested there is to create your own MapView (extend the original MapView) and override it's dispatchTouchEvent method. There you can catch any touch events and can detect if the user touched/moved the map.

Performance issue on Android's MapView Navigation-App

I've got a question on making an navigation app more faster and more stable.
The basic layer of my app is a simple mapview, covered with several overlays (2 markers for start and destination and one for the route).
My idea is to implement a thread to display the route, so that the app won't hang up during the calculation of a more complex route (like it does right now).
After implementing the thread there are no updates shows any more, maybe you can help me with a short glance at an excerpt of my code below:
private class MyLocationListener implements LocationListener {
#Override
public void onLocationChanged(Location loc) {
posUser = new GeoPoint((int) (loc.getLatitude() * 1E6), (int) (loc
.getLongitude() * 1E6));
new Thread(){
public void run(){
mapView.invalidate();
// Erase old overlays
mapView.getOverlays().clear();
// Draw updated overlay elements and adjust basic map settings
updateText();
if(firstRefresh){
adjustMap();
firstRefresh = false;
}
getAndPaintRoute();
drawMarkers();
}
};
}
Some features have been summarized to a method like "drawMarkers()" or "updateText()"...(they don't need any more attention ;-))
When are you actually asking for the thread to run? I only see code for creating it. If you did, you'd discover that only the main (UI) thread is allowed to update, as RPond notes.
Instead, split off your work and post the results back to the main thread via a Handler.
You can only update the UI on the UI thread so I think that is the problem. Check out this article on threading for solutions to this problem.
I may be barking up the wrong tree here, but my guess is that it's something to do with the fact that you're making changes to the MapView on a thread that isn't the UI thread.
I'd expect this to result in one of these possibilities:
Your changes are throwing an exception that you're not seeing (again possibly because it's on another thread)
Your changes are being ignored because they're being made on the wrong thread.
The map's being updated but your UI thread doesn't know it needs to redraw the map.
Hope this helps - at least by pointing you in vaguely the right direction.
One would hope you're not using the Google MapView for rendering your navigation, since from my reading of the terms and conditions, navigation is not included in the acceptable use policy.
From the Android Maps APIs Terms of Service
Under this Section 8, you must not (nor may you permit anyone else to):
...
8.7. use the Service or Content with any products, systems, or applications for or in connection with (a) real time navigation or route guidance based on position input from a sensor (including but not limited to any visual or audible turn-by-turn route guidance);
...

Categories

Resources