Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert GMSVisibleRegion to CLRegion or MKCoordinateRegion

I am using the GoogleMaps SDK and currently I am trying to convert a GMSVisibleRegion to a CLRegion.

GMSVisibleRegion is defined as:

typedef struct {
  CLLocationCoordinate2D nearLeft;
  CLLocationCoordinate2D nearRight;
  CLLocationCoordinate2D farLeft;
  CLLocationCoordinate2D farRight;
} GMSVisibleRegion;

What is the fastest way to do so?

Unfortunately it is difficult to understand what the developer meant with the naming "near" and "far". I think this comment can also be useful:

/**
 * Returns the region (four location coordinates) that is visible according to
 * the projection.
 *
 * The visible region can be non-rectangular. The result is undefined if the
 * projection includes points that do not map to anywhere on the map (e.g.,
 * camera sees outer space).
 */
 - (GMSVisibleRegion)visibleRegion;

Thanks a lot!

EDIT: Ok my first step was to create a MKCoordinateRegion of a GMSVisibleRegion.

I propose the following code to transform a a GMSVisibleRegion to a MKCoordinateRegion. Any objections.


+ (MKCoordinateRegion)regionForCenter:(CLLocationCoordinate2D)center andGMSVisibleRegion:(GMSVisibleRegion)visibleRegion
{
    CLLocationDegrees latitudeDelta = visibleRegion.farLeft.latitude - visibleRegion.nearLeft.latitude;
    CLLocationDegrees longitudeDelta = visibleRegion.farRight.longitude - visibleRegion.farLeft.longitude;
    MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);

    return MKCoordinateRegionMake(center, span);
}
like image 311
Robert Weindl Avatar asked Nov 28 '22 14:11

Robert Weindl


1 Answers

My guess is that 'near' is for the corners of the view at the bottom of the screen, and 'far' is for the corners at the top of the screen. This is because if you've tilted the view, then the bottom corners are nearest to the camera, and the top corners are furthest from the camera.

One way to turn this into a CLRegion might be to use the camera's target as the centre, and then calculate the radius from the maximum distance to the four corners. This might not be the tightest fitting circle over the region, but since a circle can't fit the quadrilateral of the view anyway, it may be close enough.

Here's a helper function to calculate the distance in metres between two CLLocationCoordinate values:

double getDistanceMetresBetweenLocationCoordinates(
    CLLocationCoordinate2D coord1, 
    CLLocationCoordinate2D coord2)
{
    CLLocation* location1 = 
        [[CLLocation alloc] 
            initWithLatitude: coord1.latitude 
            longitude: coord1.longitude];
    CLLocation* location2 = 
        [[CLLocation alloc] 
            initWithLatitude: coord2.latitude 
            longitude: coord2.longitude];

    return [location1 distanceFromLocation: location2];
}

Then the CLRegion can be calculated like this:

GMSMapView* mapView = ...;
...
CLLocationCoordinate2D centre = mapView.camera.target;
GMSVisibleRegion* visibleRegion = mapView.projection.visibleRegion;

double nearLeftDistanceMetres = 
    getDistanceMetresBetweenLocationCoordinates(centre, visibleRegion.nearLeft);
double nearRightDistanceMetres = 
    getDistanceMetresBetweenLocationCoordinates(centre, visibleRegion.nearRight);
double farLeftDistanceMetres = 
    getDistanceMetresBetweenLocationCoordinates(centre, visibleRegion.farLeft);
double farRightDistanceMetres = 
    getDistanceMetresBetweenLocationCoordinates(centre, visibleRegion.farRight);
double radiusMetres = 
    MAX(nearLeftDistanceMetres, 
    MAX(nearRightDistanceMetres, 
    MAX(farLeftDistanceMetres, farRightDistanceMetres)));

CLRegion region = [[CLRegion alloc] 
    initCircularRegionWithCenter: centre radius: radius identifier: @"id"];

UPDATE:

Regarding your update for MKCoordinateRegion, your example code may not work. If the map has been rotated 90 degrees, then farLeft and nearLeft will have the same latitude, and farRight and farLeft will have the same longitude, and so your latitude and longitude deltas would be zero.

You would need to loop over all four of the farLeft, farRight, nearLeft, nearRight, calculate the min and max of the latitude and longitude of each, and then calculate the delta from that.

The Google Maps SDK for iOS includes a helper class which already does some of this for you - GMSCoordinateBounds. It can be initialized with a GMSVisibleRegion:

GMSMapView* mapView = ...;
....
GMSVisibleRegion visibleRegion = mapView.projection.visibleRegion;
GMSCoordinateBounds bounds = 
    [[GMSCoordinateBounds alloc] initWithRegion: visibleRegion];

The GMSCoordinateBounds then has northEast and southWest properties which define the bounds. So you could calculate the deltas as follows:

CLLocationDegrees latitudeDelta = 
    bounds.northEast.latitude - bounds.southWest.latitude;
CLLocationDegrees longitudeDelta = 
    bounds.northEast.longitude - bounds.southWest.longitude;

You could also calculate the centre from the bounds, and therefore the MKCoordinateRegion:

CLLocationCoordinate2D centre = CLLocationCoordinate2DMake(
    (bounds.southWest.latitude + bounds.northEast.latitude) / 2,
    (bounds.southWest.longitude + bounds.northEast.longitude) / 2);
MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
return MKCoordinateRegionMake(centre, span);
like image 183
Saxon Druce Avatar answered Dec 06 '22 00:12

Saxon Druce