I have a list of LatLng points that I want to show as markers on a GoogleMap. I am currently computing a camera view that fits all of my points the following way:
First I compute a LatLngBounds with all my points.
Iterator<LatLng> i = items.iterator();
LatLngBounds.Builder builder = LatLngBounds.builder();
while (i.hasNext()) {
builder.include(i.next());
}
LatLngBounds bounds = builder.build();
Then I use the appropriate method:
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, padding);
The update offers a good view over all my points, with some nice padding.
Question
I’d like to apply different values for paddingTop, paddingBottom, paddingLeft and paddingRight. Namely, I have nothing on both sides, then I have a floating toolbar at the top (about 50dp
) and a floating card at the bottom (about 200dp
).
If I add no padding, some of the points will be covered by the top toolbar or by the bottom card.
If I add the maximum padding (200dp
), then there are huge gaps on top, left and right. Any idea?
__________________________
| ___________o__________ |
| | | |
| |____________________|o|
| o |
| o |
| o |
| MAP |
| o |
| o |
| ______________________ |
| | | |
|o| | |
| | |o|
| |____________________| |
|____________________o___|
My only idea right now is, get the map width in pixels, get the map width in Lng (not well defined), find the pixels/lng ratio, convert desired padding from pixels to lng, and recompute LatLngBounds adding a fake point (one for each side) that is that distant from my original bounds.
This seems to work, but I’ll wait for a better solution if any.
You need to pass width and height (in pixels) of the view holding the map, so that we can compute a pixel to projected coordinates coefficient.
Then you pass desired padding in a Rect
object.
If you have lots of points this is probably best suited for running in a background task.
public static CameraUpdate computeCameraView(final List<LatLng> items,
final GoogleMap map, final Rect padding,
final int viewWidth, final int viewHeight) {
// Compute bounds without padding
Iterator<LatLng> i = items.iterator();
LatLngBounds.Builder builder = LatLngBounds.builder();
while (i.hasNext()) {
builder.include(i.next());
}
LatLngBounds bounds = builder.build();
// Create a first CameraUpdate and apply
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, 0);
map.moveCamera(cameraUpdate);
// Convert padding to lat/lng based on current projection
bounds = map.getProjection().getVisibleRegion().latLngBounds;
double mapWidth = bounds.northeast.longitude - bounds.southwest.longitude;
double mapHeight = bounds.northeast.latitude - bounds.southwest.latitude;
double pixelToLng = mapWidth / viewWidth;
double pixelToLat = mapHeight / viewHeight;
padding.top = (int) (padding.top * pixelToLat);
padding.bottom = (int) (padding.bottom * pixelToLat);
padding.left = (int) (padding.left * pixelToLng);
padding.right = (int) (padding.right * pixelToLng);
// Now padding holds insets in lat/lng values.
// Let's create two fake points and bound
LatLng northEast = new LatLng(bounds.northeast.latitude + padding.top,
bounds.northeast.longitude + padding.right);
LatLng southWest = new LatLng(bounds.southwest.latitude - padding.bottom,
bounds.southwest.longitude - padding.left);
LatLngBounds newBounds = new LatLngBounds.Builder()
.include(northEast).include(southWest).build();
return CameraUpdateFactory.newLatLngBounds(newBounds, 0);
}
The weak point here is double mapWidth = bounds.northeast.longitude - bounds.southwest.longitude
. You can’t compute longitude like that, because it’s a circular variable (i.e. there are going to be issues if the visible region crosses the 180 meridian). I’ll look into it.
This should be enough:
public static double computeLngDistance(LatLng east, LatLng west) {
if (east.longitude <= 0 && west.longitude >= 0) {
// We are crossing the 180 line.
return (east.longitude + 180d) + (180d - west.longitude);
} else {
return east.longitude - west.longitude;
}
}