3

I would like to have something like a radar screen that centers on where the user is while zooming just enough to include a target point with the v2 api. Right now I'm using

bounds = new LatLngBounds.Builder().include(destPos)
                .include(yourPos).build();
        map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));

but this centers at some point between the two points and zooms to include both. Is there a way as easy as this to do what I want? Or do I have to more or less start from scratch and do some math (e.g. calculate the distance between the two points, calculate the lat/lng's for LatLngBounds so that user is at the center of the defined rectangle and the edges of the rectangle include the destination -- taking into consideration the map/screen dimensions)?

Here's what I have:

enter image description here

Here's what I want:

enter image description here

Cartesian Theater
  • 1,920
  • 2
  • 29
  • 49
  • @MES-please add screen shots if possible. – TheFlash May 04 '13 at 04:10
  • You could center the map in the first point and then zoom out until the second point is visible. I haven't tried this myself but maybe you can use `GoogleMap#getProjection().getVisibleRegion().latLngBounds` to get the current visible region of the map and then ask if the second point is within the bounds using `LatLngBounds#contains()`. If the second point isn't visible then you can zoom out and check again until the point is visible. I'll try this for one of my projects and if it works I will post an answer. – rubenlop88 May 04 '13 at 05:08
  • 1
    I actually tried that. The projection doesn't update when the camera changes, however. – Cartesian Theater May 04 '13 at 05:32

3 Answers3

5

Your own answer is overcomplicated.

You need to calculate point that is on the other side of your position and include that in LatLngBounds.

LatLng otherSidePos = new LatLng(2 * yourPos.latitude - destPos.latitude, 2 * yourPos.longitude - destPos.longitude);
bounds = new LatLngBounds.Builder().include(destPos)
                .include(otherSidePos).build();
        map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, padding));

Note: you don't want to hardcode 50 pixels as padding. It will look different on different devices. Use density independent pixels instead.

MaciejGórski
  • 22,187
  • 7
  • 70
  • 94
  • Definitely more elegant; I'll use this instead. BTW, I wasn't padding 50pixels, but 1.5 the distance in meters for the latlngbounds. – Cartesian Theater May 05 '13 at 17:32
  • I would suggest (because for me your solution crashed as map hasn't layout yet) to use another method to animate: `map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, metrics.widthPixels, metrics.heightPixels, padding));` To get metrics one may use: `DisplayMetrics metrics=new DisplayMetrics();` `getWindowManager().getDefaultDisplay().getMetrics(metrics);` – Nat Sep 01 '13 at 17:02
  • @Vive If you happen to call 2 parameters version before layout is complete it will crash [as stated in the documentation](https://developers.google.com/maps/documentation/android/reference/com/google/android/gms/maps/CameraUpdateFactory#newLatLngBounds%28com.google.android.gms.maps.model.LatLngBounds,%20int%29). You are free to use 4 params version or call it after layout to complete. – MaciejGórski Sep 01 '13 at 17:59
  • Yes I know, I've read about this. I wanted to supply the solution in case someone learning, as me, will have similar problem - he won't have to search. That's why the answer wasn't addressed to you - I know you're acknowledged with the problem, it was for other newbies like me. – Nat Sep 01 '13 at 18:19
1

I figured out how to do it the hard way as given above. I settled for a square area for the LatLngBounds to simplify getting the bearing number (which is okay with me as long as it fits in the screen: you can figure out what the bearing would be from the center to the corners of non-square rectangles with some more trig). I also added a bit of padding, as the distance to between the points will be the same as the diagonal from the center to the northeast corner, and that will be greater than the distance from the center to the top edge which would mean the second point could be above the top edge and not visible (if it were, say, due north). I borrowed heavily from http://www.movable-type.co.uk/scripts/latlong.html and calculating Lat and Long from Bearing and Distance,

Here's my method for getting the LatLngBounds:

public LatLngBounds getBounds(double lat1, double lng1, double lat2,
        double lng2) {

    // defines a square area with point lat1,lng1 at center and includes
    // point
    // lat2, long2 with a buffer between edge and second point

    // get distance between two points
    float[] results = new float[1];

    Location.distanceBetween(lat1, lng1, lat2, lng2, results);
    double d = results[0];

    d = (d * 1.5); // add padding. The shortest distance to an edge of the
//square box is d * cos(45 degrees). Thus it's possible that the second point will be out of the box. To compensate I make the radius larger (d * 1/cos(45) = d * 1.41). I put a bit extra so that the second point isn't right on the edge of the screen. 

    long R = 6371000; // distance of earth's radius in meters

    d = d /(double) R;

    lat1 = Math.toRadians(lat1); // Current lat point converted to radians
    lng1 = Math.toRadians(lng1); // Current long point converted to radians

    // calculate northeast corner of LatLngBounds

    double brng = Math.toRadians(45); // bearing from center to northeast in
                                        // radians

    double resultLat1 = Math.asin(Math.sin(lat1) * Math.cos(d)
            + Math.cos(lat1) * Math.sin(d) * Math.cos(brng));

    double resultLng1 = lng1
            + Math.atan2(Math.sin(brng) * Math.sin(d) * Math.cos(lat1),
                    Math.cos(d) - Math.sin(lat1) * Math.sin(resultLat1));

    resultLat1 = Math.toDegrees(resultLat1);
    resultLng1 = Math.toDegrees(resultLng1);

    Log.i("My Code", "resultLat1: " + resultLat1 + " resultLng1: " + resultLng1);

    LatLng northEast = new LatLng(resultLat1, resultLng1);

    // calculate southwest corner of LatLngBounds. Everything is the same
    // except the bearing value

    brng = Math.toRadians(225); // bearing from center to southwest corner
                                // in radians
    double resultLat2 = Math.asin(Math.sin(lat1) * Math.cos(d)
            + Math.cos(lat1) * Math.sin(d) * Math.cos(brng));

    double resultLng2 = lng1
            + Math.atan2(Math.sin(brng) * Math.sin(d) * Math.cos(lat1),
                    Math.cos(d) - Math.sin(lat1) * Math.sin(resultLat2));

    resultLat2 = Math.toDegrees(resultLat2);
    resultLng2 = Math.toDegrees(resultLng2);

    Log.i("My Code", "resultLat2: " + resultLat2 + " resultLng2: " + resultLng2);

    LatLng southWest = new LatLng(resultLat2, resultLng2);

    LatLngBounds bounds = new LatLngBounds(southWest, northEast);

    return bounds;
}
Community
  • 1
  • 1
Cartesian Theater
  • 1,920
  • 2
  • 29
  • 49
1

For zooming to include all markers without changing the camera target (map centre).

LatLngBounds.Builder builder = new LatLngBounds.Builder();
for (Marker marker : markers) {
    builder.include(marker.getPosition());
}
LatLngBounds bounds = builder.build();

LatLng currentLoc = _googleMapView.getCameraPosition().target;

//make sure that centre location doesn't change
double deltaLat = Math.max(Math.abs(bounds.southwest.latitude - currentLoc.latitude),
                Math.abs(bounds.northeast.latitude - currentLoc.latitude));

double deltaLon = Math.max(Math.abs(bounds.southwest.longitude - currentLoc.longitude),
                Math.abs(bounds.northeast.longitude - currentLoc.longitude));

LatLngBounds.Builder displayBuilder = new LatLngBounds.Builder();
displayBuilder.include(currentLoc); //not necessary but hey
displayBuilder.include(new LatLng(currentLoc.latitude + deltaLat, currentLoc.longitude + deltaLon));
displayBuilder.include(new LatLng(currentLoc.latitude - deltaLat, currentLoc.longitude - deltaLon));

_googleMapView.moveCamera(CameraUpdateFactory.newLatLngBounds(displayBuilder.build(), 0));
Steven Veltema
  • 2,140
  • 15
  • 18