android developer
android developer

Reputation: 116000

How to focus on location on Google maps, considering a view is on top of it?

Background

Suppose I have a Google maps view, and another view on top of it, that covers a part of it, hiding some content of the map.

The problem

I need to make the "camera" of the map, to focus and have a marker on a coordinate , yet let it all be in the middle of the visible part of the map.

Something like this:

enter image description here

The original code was focusing on (about) the center of the entire screen, making the marker almost invisible (as the bottom view covers it).

Thing is, I can't find the proper way to set the correct value to the Y coordinate of the map itself (meaning latitude).

What I've tried

I tried, given the height of the bottom view, and the coordinate that I've put the marker on, to calculate the delta (yet of course not change the marker itself) :

    final float neededZoom = 6.5f;
    int bottomViewHeight = bottomView.getHeight();
    LatLng posToFocusOn = ...;
    final Point point = mMap.getProjection().toScreenLocation(posToFocusOn);
    final float curZoom = mMap.getCameraPosition().zoom;
    point.y += bottomViewHeight * curZoom / neededZoom;
    posToFocusOn = mMap.getProjection().fromScreenLocation(point);
    final CameraUpdate cameraPosition = CameraUpdateFactory.newCameraPosition(new Builder().target(posToFocusOn).zoom(neededZoom).build());

Sadly, this focuses way above the marker.

The question

What's wrong with what I wrote? What can I do to fix it?

Upvotes: 10

Views: 2035

Answers (2)

android developer
android developer

Reputation: 116000

ok, I've found a workaround, which I think works on all devices (tested on 3, each with a different screen resolution and size) :

I've measured how many pixels (and then converted to DP) a change of one degree has on the marker itself.

From this, I measured the height of each view, and calculated the delta needed to move the camera.

In my case, it's this way (supposing the zoom is 6.5f) :

    //measured as 223 pixels on Nexus 5, which has xxhdpi, so divide by 3
    final float oneDegreeInPixels = convertDpToPixels( 223.0f / 3.0f);
    final float mapViewCenter = mapViewHeight / 2.0f;
    final float bottomViewHeight = ...;
    final float posToFocusInPixelsFromTop = (mapViewHeight - bottomViewHeight) / 2.0f ;// can optionally add the height of the view on the top area
    final float deltaLatDegreesToMove = (mapViewCenter - posToFocusInPixelsFromTop) / oneDegreeInPixels;
    LatLng posToFocusOn = new LatLng(latitude - deltaLatDegreesToMove, longitude);
    final CameraUpdate cameraPosition = CameraUpdateFactory.newCameraPosition(new Builder().target(posToFocusOn).zoom(neededZoom).build());

And it worked.

I wonder if it can be adjusted to support any value of zoom.

Upvotes: 7

antonio
antonio

Reputation: 18262

Your code is almost right, but it goes above the marker because you are taking into account bottomViewHeight when computing point.y instead of bottomViewHeight/2 (When your view's size is 200px, you only need to displace the map 100px to recenter it):

point.y += (bottomViewHeight / 2) * curZoom / neededZoom;

Update:

This is a more general approach taht takes into account the map bounds and calculates a new map bounds according to the height of your bottomView. This is zoom independent.

public void recenter() {
    LatLngBounds mapBounds = mMap.getProjection().getVisibleRegion().latLngBounds;
    Point nothEastPoint = mMap.getProjection().toScreenLocation(mapBounds.northeast);
    Point souhWestPoint = mMap.getProjection().toScreenLocation(mapBounds.southwest);

    Point newNorthEast = new Point(nothEastPoint.x, nothEastPoint.y + bottomView.getHeight() / 2);
    Point newSouhWestPoint = new Point(souhWestPoint.x, souhWestPoint.y + bottomView.getHeight() / 2);

    LatLngBounds newBounds = LatLngBounds.builder()
            .include(mMap.getProjection().fromScreenLocation(newNorthEast))
            .include(mMap.getProjection().fromScreenLocation(newSouhWestPoint))
            .build();

    mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(newBounds, 0));
}

Note that each time you call recenter() the map will move.

Upvotes: 2

Related Questions