Search code examples
androidgoogle-mapsgoogle-maps-api-2android-maps-utils

Google Maps Android Heatmap Utility showing white tiles


I'm using the Google Maps Android Heatmap Utility (https://developers.google.com/maps/documentation/android/utility/heatmap) with the Google Maps API v2. This utility is basically just one class, implementing a TileProvider that can be added to a Map using map.addTileOverlay. The code is public on GitHub:

https://github.com/googlemaps/android-maps-utils/blob/master/library/src/com/google/maps/android/heatmaps/HeatmapTileProvider.java

Now when I run their demo app (https://developers.google.com/maps/documentation/android/utility/setup), I get a weird bug. Basically, when the viewed data is shown in the middle of the screen, everything works. But when you start to drag the screen and the data gets to the edge of the screen, the tile containing the data turns white.

Only tiles in the center are shown correctly

Once I scroll the data back towards the center, everything looks fine again.

Im pretty sure this is not a bug in the tile generation code of the TileProvider, as its getTile-Method never actually returns any white or otherwise incorrect tiles (I've checked). I had a few people try out the demo app and they could all reproduce the problem.

My questions are:

  1. Is it safe to assume that this is a bug in the Maps API v2 TileOverlay?
  2. Has anyone had this problem before? Any solutions?

Solution

  • It is a problem with GoogleMap. HeatmapTileProvider in android-maps-utils method getTile returns TileProvider.NO_TILE when you're scrolling map and GoogleMap clears all tiles with Heats. I added a android-maps-utils library to project (none gradle dependency) and added this code

    private final static Tile BLANK_TILE = new Tile(0, 0, new byte[0]);
     public Tile getTile(int x, int y, int zoom) {
            boolean skipImage = false;
            ...
             if (!tileBounds.intersects(paddedBounds)) {
    //            return TileProvider.NO_TILE;
                skipImage = true;
            }
            ...
            if (points.isEmpty()) {
    //            return TileProvider.NO_TILE;
                skipImage = true;
            }
     if (!skipImage) {
                // Quantize points
                double[][] intensity = new double[TILE_DIM + mRadius * 2][TILE_DIM + mRadius * 2];
                for (WeightedLatLng w : points) {
                    Point p = w.getPoint();
                    int bucketX = (int) ((p.x - minX) / bucketWidth);
                    int bucketY = (int) ((p.y - minY) / bucketWidth);
                    intensity[bucketX][bucketY] += w.getIntensity();
                }
                // Quantize wraparound points (taking xOffset into account)
                for (WeightedLatLng w : wrappedPoints) {
                    Point p = w.getPoint();
                    int bucketX = (int) ((p.x + xOffset - minX) / bucketWidth);
                    int bucketY = (int) ((p.y - minY) / bucketWidth);
                    intensity[bucketX][bucketY] += w.getIntensity();
                }
    
                // Convolve it ("smoothen" it out)
                double[][] convolved = convolve(intensity, mKernel);
    
                // Color it into a bitmap
                bitmap = colorize(convolved, mColorMap, mMaxIntensity[zoom]);
    
                // Convert bitmap to tile and return
                return convertBitmap(bitmap);
            } else {
                return BLANK_TILE;
            }