2

I'm building an app where I load data from the API in onCameraIdle. The request needs the target location and a radius in which to give me the results.

The code I use to get the radius from the current camera is:

/**
 * @param map The Map on which to compute the radius.
 * @return The circle radius of the area visible on map, in meters.
 */
public float getRadiusVisibleOnMap(GoogleMap map) {
    VisibleRegion visibleRegion = map.getProjection().getVisibleRegion();

    LatLng farRight = visibleRegion.farRight;
    LatLng farLeft = visibleRegion.farLeft;
    LatLng nearRight = visibleRegion.nearRight;
    LatLng nearLeft = visibleRegion.nearLeft;

    float[] distanceWidth = new float[1];
    Location.distanceBetween(
            (farRight.latitude + nearRight.latitude) / 2,
            (farRight.longitude + nearRight.longitude) / 2,
            (farLeft.latitude + nearLeft.latitude) / 2,
            (farLeft.longitude + nearLeft.longitude) / 2,
            distanceWidth
    );


    float[] distanceHeight = new float[1];
    Location.distanceBetween(
            (farRight.latitude + nearRight.latitude) / 2,
            (farRight.longitude + nearRight.longitude) / 2,
            (farLeft.latitude + nearLeft.latitude) / 2,
            (farLeft.longitude + nearLeft.longitude) / 2,
            distanceHeight
    );

    float radius;

    if (distanceWidth[0] > distanceHeight[0]) {
        radius = distanceWidth[0];
    } else {
        radius = distanceHeight[0];
    }

    return radius;
}

I got it from here

There are some cases where I need to get the radius knowing only the zoom level, so that the info is already loaded on the map when the zoom animation is finished.

I tried to adjust the code from here to get the radius knowing the zoom level instead of the other way around.

public int getZoomLevel(Circle circle) {
    if (circle != null) {
        double radius = circle.getRadius();
        double scale = radius / 500;
        zoomLevel = (int) (16 - Math.log(scale) / Math.log(2));
    }
    return zoomLevel;
}

I got the formula: formula

The formula is incorrect. It doesn't output the right dimensions. For a zoom level of 14, I get a radius dimension of 2.697331km using the camera's VisibleRegion. If I calculate the radius using the formula, the result is 0.008km.

Community
  • 1
  • 1
Vlad
  • 988
  • 12
  • 18
  • What is the problem you are facing here? – Sreehari May 10 '17 at 13:08
  • The formula I got to for calculating the radius is incorrect. It doesn't output the right dimensions. For a zoom level of 14, I get a radius dimension of 2.697331km using the camera's VisibleRegion. If I calculate the radius using the formula, the result is 0.008km. – Vlad May 10 '17 at 13:14
  • Do you have more than one latitude longitude on the visual base? If then I can give you the logic to calculate radius out of that. I mean zoom based on more than one lat / lon – Sreehari May 10 '17 at 13:18
  • I'm not sure what you mean. I need the radius before I initiate the following command: map.animateCamera(CameraUpdateFactory.newLatLngZoom(location, zoomLevel), callback); I have the location : LatLng and zoomLevel : float – Vlad May 10 '17 at 13:24
  • I mean if you have Lat/Lon for Location 1 and Lat/Lon for Location 2 and Lat/Lon for Location 3, Then I can give you the formula to calculate the zoom based on all these locations – Sreehari May 10 '17 at 13:27
  • No, I do not have multiple locations. I do not want to set the camera in such way that 3 locations are shown simultaneously. For that I would use a LatLngBounds where to hold the coordinates and CameraUpdateFactory.newLatLngBounds. I need to know the camera's VisibleRegion before the zoom animation is finished. Thanks anyway. – Vlad May 10 '17 at 13:33

2 Answers2

4

Looks like best practice for you is to find a minimal radius value that fits the map rectangle inside.

You were almost there, use the modified function below which uses Pythagorean theorem to find the minimal radius required to contain the map's "rectangle".


enter image description here


@Override
public void onMapReady(GoogleMap googleMap) {
    this.googleMap = googleMap;
    googleMap.setOnMarkerClickListener(this);

    googleMap.setOnCameraIdleListener(() -> {
        CameraPosition cameraPosition = googleMap.getCameraPosition();
        double radiusInMeters = getMapVisibleRadius();
        double radiusInKilometers = radiusInMeters / 1000;
        double latitude = cameraPosition.target.latitude;
        double longitude = cameraPosition.target.longitude;

        // TODO you take it from here, 
        // I use firebase to get relevant markers based on the lat,long,radius

    });
}


private double getMapVisibleRadius() {
    VisibleRegion visibleRegion = googleMap.getProjection().getVisibleRegion();

    float[] distanceWidth = new float[1];
    float[] distanceHeight = new float[1];

    LatLng farRight = visibleRegion.farRight;
    LatLng farLeft = visibleRegion.farLeft;
    LatLng nearRight = visibleRegion.nearRight;
    LatLng nearLeft = visibleRegion.nearLeft;

    Location.distanceBetween(
            (farLeft.latitude + nearLeft.latitude) / 2,
            farLeft.longitude,
            (farRight.latitude + nearRight.latitude) / 2,
            farRight.longitude,
            distanceWidth
    );

    Location.distanceBetween(
            farRight.latitude,
            (farRight.longitude + farLeft.longitude) / 2,
            nearRight.latitude,
            (nearRight.longitude + nearLeft.longitude) / 2,
            distanceHeight
    );

    double radiusInMeters = Math.sqrt(Math.pow(distanceWidth[0], 2) + Math.pow(distanceHeight[0], 2)) / 2;
    return radiusInMeters;
}
Jossef Harush Kadouri
  • 32,361
  • 10
  • 130
  • 129
0

As an update for everyone who is using Maps Compose Library I can recommend this extension function on CameraPositionState to directly get the visible radius:

fun CameraPositionState.visibleRadius(): Double {
    this.projection?.visibleRegion?.let { visibleRegion ->
        val distanceWidth = FloatArray(1)
        val distanceHeight = FloatArray(1)
        val farRight = visibleRegion.farRight
        val farLeft = visibleRegion.farLeft
        val nearRight = visibleRegion.nearRight
        val nearLeft = visibleRegion.nearLeft

        Location.distanceBetween(
            (farLeft.latitude + nearLeft.latitude) / 2,
            farLeft.longitude,
            (farRight.latitude + nearRight.latitude) / 2,
            farRight.longitude,
            distanceWidth
        )
        Location.distanceBetween(
            farRight.latitude,
            (farRight.longitude + farLeft.longitude) / 2,
            nearRight.latitude,
            (nearRight.longitude + nearLeft.longitude) / 2,
            distanceHeight
        )
        return sqrt(
            distanceWidth[0].toDouble().pow(2.0) + distanceHeight[0].toDouble().pow(2.0)
        ) / 2
    }
    return -1.0
}

Later you can then use it like this in you UI code:

LaunchedEffect(cameraPositionState.isMoving) {
    if (!cameraPositionState.isMoving) {
        viewModel.onUpdateVisibleRadius(
            visibleRadius = cameraPositionState.visibleRadius(),
        )
    }
}