0

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);
        }
    }
}
Dusan
  • 3,284
  • 6
  • 25
  • 46

2 Answers2

0

It is hard to help you without source code of findTheMarkersInBounds() and displayTheMarkers(), but seems, you need different approach to increase performance, for example:

  1. improve your findTheMarkersInBounds() logic if it possible;

  2. 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;

  3. 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):

  1. pass approximately max size of markersToDisplay as constructor parameter: ArrayList<MarkerObject> markersToDisplay = new ArrayList<MarkerObject>(1000);

  2. Instead for (int i=0; i<markersToDisplay.size(); i++) {

use for (MarkerObject mo: markersToDisplay) {

  1. 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.

Andrii Omelchenko
  • 13,183
  • 12
  • 43
  • 79
  • I updated my question. Added source code of my methods. I'll give a try to the idea with the separate thread. – Dusan Dec 08 '20 at 20:20
0

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.

Dusan
  • 3,284
  • 6
  • 25
  • 46