GoogleMap snapshot method returns white image
Asked Answered
O

4

6

I'm trying to grab a screenshot of a Google Maps V2 map using the API mentioned here, however this seems to always return a white image with the Google logo at the bottom. If it wasn't for the logo I would have been sure this isn't working at all but the logo means that something is happening and a map just isn't showing. This is roughly what I'm doing:

mv.setVisibility(View.VISIBLE);
mv.getMap().snapshot(new GoogleMap.SnapshotReadyCallback() {
    public void onSnapshotReady(Bitmap snapshot) {
        synchronized(finished) {
            finished[0] = snapshot;
            finished.notify();
        }
    }
});

I tried multiple different approaches including drawing the image to a different image and various other attempts.

The only major difference is that I am using a MapView and not a MapFragment due to some technical issues I can't switch to using fragments here.

Odlo answered 16/3, 2014 at 19:23 Comment(1)
You have to wait for the map to load with the OnMapLoadedCallback then request a snapshot.Algetic
A
7

To take a snapshot you wait for the map to load then take a snapshot.

details: implement SnapshotReadyCallback and OnMapLoadedCallback.

import com.google.android.gms.maps.GoogleMap.SnapshotReadyCallback;
import com.google.android.gms.maps.GoogleMap.OnMapLoadedCallback;

public class KmlReader extends ActionBarActivity implements
     SnapshotReadyCallback,
    OnMapLoadedCallback {

...

 if (mMap == null) {
// Try to obtain the map from the SupportMapFragment.
// mMap = mMapFragment.getMap();
// // Check if we were successful in obtaining the map.

// Try to obtain the map from the SupportMapFragment.
mMap = ((SupportMapFragment) getSupportFragmentManager()
        .findFragmentById(R.id.map)).getMap();
// use the settings maptype

// Check if we were successful in obtaining the map.
if (mMap != null) {

mMap.setOnMapLoadedCallback(this);

...

@Override
public void onMapLoaded() {
   if (mMap != null) {
        mMap.snapshot(this);
   }
}

You have a real bitmap when this fires.

@Override
public void onSnapshotReady(Bitmap bm) {
    if (CheckStorage.isExternalStorageWritable()) {
        int newHeight = 96;
        int newWidth = 96;

        int width = bm.getWidth();

        int height = bm.getHeight();

        float scaleWidth = ((float) newWidth) / width;

        float scaleHeight = ((float) newHeight) / height;

        // create a matrix for the manipulation

        Matrix matrix = new Matrix();

        // resize the bit map

        matrix.postScale(scaleWidth, scaleHeight);

        // recreate the new Bitmap

        Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height,
                matrix, false);
Algetic answered 16/3, 2014 at 23:3 Comment(4)
Thanks, the map is showing when I try to take the shot. Does that mean its loaded?Odlo
I just tried that and got pretty much the same result, it still doesn't work. Thanks for the effort.Odlo
Sorry it didn't work for you. It works for me and I was able to duplicate your issue by taking a snapshot before the onMapLoaded method fired.Algetic
While this wasn't what solved my issue I've accepted it as the answer since for most people this will be the correct answer. What solved it for us is a bit of internal logic which I don't really understand and is completely unrelated to Android.Odlo
S
4

I needed to take a snapshot on a button press. So in the button handler I put the following code:

final SnapshotReadyCallback snapReadyCallback = new SnapshotReadyCallback() {
        Bitmap bitmap;
        @Override
        public void onSnapshotReady(Bitmap snapshot) {
            bitmap = snapshot;
            try {
                //do something with your snapshot
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

GoogleMap.OnMapLoadedCallback mapLoadedCallback = new GoogleMap.OnMapLoadedCallback() {
        @Override
        public void onMapLoaded() {
            gmap.snapshot(snapReadyCallback);
        }
};
gmap.setOnMapLoadedCallback(mapLoadedCallback);

What happens here is that I call the Map.setOnMapLoadedCallback() which will wait until the map has loaded (onMapLoaded()) and the calls the Map.snapshot(). This will guarantee that the map is allways loaded and you will not get the white image.

Stellastellar answered 7/3, 2016 at 11:31 Comment(0)
A
3

That works for me. I hope it helps you:

SnapshotReadyCallback callback = new SnapshotReadyCallback() {
                Bitmap bitmap;

                @Override
                public void onSnapshotReady(Bitmap snapshot) {
                    // TODO Auto-generated method stub
                    bitmap = snapshot;
                    try {
                        String mPath = Environment.getExternalStorageDirectory().toString();
                        FileOutputStream out = new FileOutputStream(mPath + "/ "+ nom + ".png");
                        bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
                        Toast.makeText(getActivity(), "Capture OK", Toast.LENGTH_LONG).show();
                    } catch (Exception e) {
                        e.printStackTrace();
                        Toast.makeText(getActivity(), "Capture NOT OK", Toast.LENGTH_LONG).show();
                    }
                }
            };
            map.snapshot(callback);
            return true;
Arellano answered 17/3, 2014 at 0:4 Comment(1)
Thanks but I don't see much of a difference between that and my code.Odlo
C
1

I found that the map.snapshot() method only works properly under special conditions. If called too early, it will crash, if called to late and to soon after setting camera it will return a snapshot of incorrect map image. I expect snapshot to return a snapshot of the map defined by the last given camera position and polylines. However it is difficult to get the GoogleMap-class to wait will it has fetched the correct tiles. It is easy if user clicks button to do snapshot because user knows when map is ready, its more difficult if it happens programmatically.

If you have the CameraPosition (LatLng, zoom) then it works well if MapFragment is created with GoogleMapOption:

GoogleMapOptions googleMapOptions = new GoogleMapOptions();
googleMapOptions.liteMode(true).mapType(GoogleMap.MAP_TYPE_NORMAL); // if this is what you want
googleMapOptions.camera(new CameraPosition(new LatLng(56, 10), 11, 0, 0));
MapFragment.newInstance(googleMapOptions);

If you don't have the CameraPosition yet, it is more difficult. You might not have the cameraposition yet if it is computed so that given markers are contained in camera, because that computation requires GoogleMap to be initialised with width/height. (chicken-and-egg problem, or do-it-yourself).

Subclass MapFragment and overwrite onCreate

public class FixedMapFragment extends MapFragment {

    public void setOncreateListener(Listener listener) {
        this.listener = listener;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = super.onCreateView(inflater, container, savedInstanceState);
        if (listener != null) {
            listener.onCreate(v, mMapManager);
        }
        return v;
    }
}

Then create your fragment, and set your onCreateListener. The onCreateListener should update cameraPosition and set polylines etc.

When onLoad on GoogleMap is invoked we should schedule yet another onLoad which does the snapshot (or we get wrong tiles again).

private void onLoad() {
    // Set camera position and polylines here
    mMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
        @Override
        public void onMapLoaded() {
            snapshot(mSnapshotCallback);
        }
    });
}

Disadvantage: Sometimes map will flash an 'incorrect' world map on screen. To avoid this we can hide the map view until snapshat is taken using

mapView.setVisibility(VIEW.INVISIBLE);

This has been tested on google-maps 8.3.0 for android.

Cardigan answered 21/12, 2015 at 15:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.