如何使用Google Maps SDK for iOS设置地图区域?

4
如何使用Google Maps SDK设置iOS地图区域? 我想为位置和标记的半径设置缩放级别。
9个回答

16

更新:

原先的回答已经过时,从SDK版本1.2开始,您现在可以使用GMSCameraUpdate类的fitBounds:方法:

https://developers.google.com/maps/documentation/ios/reference/interface_g_m_s_camera_update


原始回答:

MapKit中的MKMapPoint类型定义了地图的二维投影。尽管投影的实际值应该是不透明的,但它们等同于缩放级别为20的像素。这可以用来将纬度/经度值转换为像素,因此可以得到一个比例尺和缩放级别。

首先定义两个位置,这些位置指定要显示的区域的边界。它们可以是边界框的相对角落,也可以只是两个位置,例如:

CLLocationCoordinate2D location1 = 
    CLLocationCoordinate2DMake(-33.8683, 151.2086); // Sydney
CLLocationCoordinate2D location2 = 
    CLLocationCoordinate2DMake(-31.9554, 115.8585); // Perth

如果你有超过两个想要包含的点,你可以自己计算它们的边界。这也可以使用GMSCoordinateBounds完成,例如:

GMSCoordinateBounds* bounds =
    [[GMSCoordinateBounds alloc]
    initWithCoordinate: CLLocationCoordinate2DMake(-33.8683, 151.2086) // Sydney
    andCoordinate: CLLocationCoordinate2DMake(-31.9554, 115.8585)]; // Perth
bounds = [bounds including: 
    CLLocationCoordinate2DMake(-12.4667, 130.8333)]; // Darwin
CLLocationCoordinate2D location1 = bounds.southWest;
CLLocationCoordinate2D location2 = bounds.northEast;

接下来,您需要获取地图视图的大小(以点为单位)。您可以使用以下代码:

float mapViewWidth = _mapView.frame.size.width;
float mapViewHeight = _mapView.frame.size.height;

但是这只适用于您已经创建了地图视图的情况。另外,如果您正在使用入门指南中的示例代码,则框架被设置为CGRectZero,因为实际大小将稍后设置以填充屏幕。在这些情况下,如果您要创建全屏地图,则可能需要像这样:

float mapViewWidth = [UIScreen mainScreen].applicationFrame.size.width;
float mapViewHeight = [UIScreen mainScreen].applicationFrame.size.height;

否则,请使用创建地图视图时的尺寸。
现在您拥有计算相机位置所需的信息:
MKMapPoint point1 = MKMapPointForCoordinate(location1);
MKMapPoint point2 = MKMapPointForCoordinate(location2);

MKMapPoint centrePoint = MKMapPointMake(
    (point1.x + point2.x) / 2,
    (point1.y + point2.y) / 2);
CLLocationCoordinate2D centreLocation = MKCoordinateForMapPoint(centrePoint);

double mapScaleWidth = mapViewWidth / fabs(point2.x - point1.x);
double mapScaleHeight = mapViewHeight / fabs(point2.y - point1.y);
double mapScale = MIN(mapScaleWidth, mapScaleHeight);

double zoomLevel = 20 + log2(mapScale);

GMSCameraPosition *camera = [GMSCameraPosition
    cameraWithLatitude: centreLocation.latitude
    longitude: centreLocation.longitude
    zoom: zoomLevel];

您可以使用此相机初始化地图视图,或将地图视图设置为此相机。

要使此代码编译,您需要将MapKit框架添加到您的项目中,并导入它:

#import <MapKit/MapKit.h>

请注意,如果您的坐标跨越日期线,此代码将无法处理环绕。例如,如果您尝试在东京和夏威夷使用此代码,则不会显示太平洋地区,而会尝试显示几乎整个世界。在纵向模式下,无法缩放到足以在左侧看到夏威夷和右侧看到东京,因此地图最终以非洲为中心,两个地点都不可见。如果您希望处理日期线上的环绕,可以修改上述代码。


嗨,Saxon。刚刚看到了这个答案。正确的缩放级别是“double zoomLevel = 21.0f + log2(mapScale)”,这可能是对的吗?我刚刚检查了GoogleMaps SDK中最大可能的缩放级别,似乎是21.0f;顺便说一下,最小缩放级别是2.0f; - Robert Weindl
嗨@xen100,20是因为MKMapPoint相当于缩放级别为20的像素。如果您的边界非常小,则mapScale最终会大于1.0,因此zoomLevel将最终大于20。如果缩放级别大于SDK的限制,则它将将缩放级别夹紧到其支持的范围内。 - Saxon Druce
谢谢,这对我很有帮助。如果您绑定了位置坐标和标记,我建议您稍微减小视图的宽度和高度,以便标记不会出现在视图边缘。 - Christopher Shortt

5

更新

所有问题都已在最新版本的Google Maps(1.5)中解决。现在可以使用标准方法[mapView_ animateWithCameraUpdate:[GMSCameraUpdate fitBounds:bounds]];,而不是下面的代码。


原始答案

[GMSCameraUpdate fitBounds] 在我的SDK版本(1.2.0)中无法给出精确结果。我使用下面的代码代替它。公式来自墨卡托投影。根据Google文档,世界在纬度上的边界为85度。

#import <stdlib.h>

-(void) animateBoundsNorth:(CGFloat)north West:(CGFloat)west South:(CGFloat)south East:(CGFloat)east Padding:(int)padding {

    CGFloat northRad = north * M_PI / 180.0;
    CGFloat northProj = logf(tanf(M_PI_4 + northRad/2.0));
    CGFloat southRad = south * M_PI / 180.0;
    CGFloat southProj = logf(tanf(M_PI_4 + southRad/2.0));
    CGFloat topRad = 85.0 * M_PI / 180.0;
    CGFloat topProj = logf(tanf(M_PI_4 + topRad/2.0));
    CGFloat zoomLat = log2f((mapView_.bounds.size.height - padding * 2) * 2 * topProj /(northProj - southProj)) - 8;
    CGFloat zoomLon = log2f((mapView_.bounds.size.width - padding * 2) * 360/(east - west)) - 8;

    GMSCameraUpdate *update = [GMSCameraUpdate setTarget:CLLocationCoordinate2DMake((north+south)/2.0, (west+east)/2.0) zoom:MIN(zoomLat, zoomLon)];


    [mapView_ animateWithCameraUpdate:update];


}

我有一个问题。如何获取“北”,“西”,“南”,“东”和“填充”? - Jogendra.Com
实际上,问题还没有解决。我正在使用Google Maps v1.7.2,当我尝试使用GMSCameraUpdate fitBounds方法时,它不起作用。最终我看到地图缩放到最大以查看整个北美洲。唯一的解决方法是使用上面的animateWithBoundsNorth:方法。 - Marc

4
萨克森·德鲁斯的回答非常好。但是,如果您想从任何位置计算半径,可以使用以下代码:
    CLLocationCoordinate2D center = CLLocationCoordinate2DMake([currentLocationLat doubleValue],[currentLocationLong doubleValue]);
float radius = 25*1000; //radius in meters (25km)

MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(center, radius*2, radius*2);

CLLocationCoordinate2D  northEast = CLLocationCoordinate2DMake(region.center.latitude - region.span.latitudeDelta/2, region.center.longitude - region.span.longitudeDelta/2);
CLLocationCoordinate2D  southWest = CLLocationCoordinate2DMake(region.center.latitude + region.span.latitudeDelta/2, region.center.longitude + region.span.longitudeDelta/2);

GMSCoordinateBounds* bounds = [[GMSCoordinateBounds alloc]
                               initWithCoordinate: northEast
                               andCoordinate: southWest];

您的回答达到了我的要求,但您能否在您的回答中添加更多说明? - Muhammad Zeeshan

3
如果您拥有Google地图“最左侧”和“最右侧”角的纬度和经度,您可以使用以下代码在Swift中显示数据。
     var region:GMSVisibleRegion = GMSVisibleRegion()
     region.nearLeft = CLLocationCoordinate2DMake(nearleflat, nearleftlong)
     region.farRight = CLLocationCoordinate2DMake(fareastlat,fareastlong)
     let bounds = GMSCoordinateBounds(coordinate: region.nearLeft,coordinate: region.farRight)
     let camera = googleMapView.cameraForBounds(bounds, insets:UIEdgeInsetsZero)
     googleMapView.camera = camera;

这个链接也可能有助于相关事项。


1
这对我的Swift有帮助。但是我得到的两个点几乎超出了屏幕,我怎么能得到更多中心的两个点:nearLeft和farRight?。谢谢你的帮助! - Dasoga

2

我目前使用的方法如下。

self.markers是一个按位置ID存储标记的字典,self.currentLocation是CLLocation2D类型,self.mapView是GMSMapView类型。

这里的数学计算是检查是否匹配宽度或高度的大小,然后根据x1 / pow(2, zoom1) = x2 / pow(2, zoom2)的事实进行缩放计算,导致zoom2 = log2(x2 * pow(2, self.mapView.camera.zoom) / x1)。

- (void)fitMarkers
{
    if (2 > self.markers.count)
    {
        [self.mapView animateToCameraPosition:[GMSCameraPosition cameraWithTarget:self.currentLocation.coordinate zoom:kZoom]];

        return;
    }

    NSArray* markers = self.markers.allValues;

    GMSCoordinateBounds* markerBounds = [[GMSCoordinateBounds alloc] initWithCoordinate:((id<GMSMarker>)markers[0]).position andCoordinate:((id<GMSMarker>)markers[1]).position];

    for (id<GMSMarker> marker in markers)
    {
        markerBounds = [markerBounds including:marker.position];
    }

    // get marker bounds in points
    CGPoint markerBoundsTopLeft = [self.mapView.projection pointForCoordinate:CLLocationCoordinate2DMake(markerBounds.northEast.latitude, markerBounds.southWest.longitude)];
    CGPoint markerBoundsBottomRight = [self.mapView.projection pointForCoordinate:CLLocationCoordinate2DMake(markerBounds.southWest.latitude, markerBounds.northEast.longitude)];

    // get user location in points
    CGPoint currentLocation = [self.mapView.projection pointForCoordinate:self.currentLocation.coordinate];

    CGPoint markerBoundsCurrentLocationMaxDelta = CGPointMake(MAX(fabs(currentLocation.x - markerBoundsTopLeft.x), fabs(currentLocation.x - markerBoundsBottomRight.x)), MAX(fabs(currentLocation.y - markerBoundsTopLeft.y), fabs(currentLocation.y - markerBoundsBottomRight.y)));

    // the marker bounds centered on self.currentLocation
    CGSize centeredMarkerBoundsSize = CGSizeMake(2.0 * markerBoundsCurrentLocationMaxDelta.x, 2.0 * markerBoundsCurrentLocationMaxDelta.y);

    // inset the view bounds to fit markers
    CGSize insetViewBoundsSize = CGSizeMake(self.mapView.bounds.size.width - kMarkerSize / 2.0 - kMarkerMargin, self.mapView.bounds.size.height - kMarkerSize / 2.0 - kMarkerSize);

    CGFloat x1;
    CGFloat x2;

    // decide which axis to calculate the zoom level with by comparing the width/height ratios
    if (centeredMarkerBoundsSize.width / centeredMarkerBoundsSize.height > insetViewBoundsSize.width / insetViewBoundsSize.height)
    {
        x1 = centeredMarkerBoundsSize.width;
        x2 = insetViewBoundsSize.width;
    }
    else
    {
        x1 = centeredMarkerBoundsSize.height;
        x2 = insetViewBoundsSize.height;
    }

    CGFloat zoom = log2(x2 * pow(2, self.mapView.camera.zoom) / x1);

    GMSCameraPosition* camera = [GMSCameraPosition cameraWithTarget:self.currentLocation.coordinate zoom:zoom];

    [self.mapView animateToCameraPosition:camera];
}

这基本上也是我想出来的解决方案。 - Scott D

1
根据GoogleMaps iOS sdk 1.9.2的最新版本发布,我们可以通过设置相机的位置、缩放级别和视角来进行设置。
GMSCameraPosition* camera = [GMSCameraPosition cameraWithLatitude:28.6100
                                                            longitude:77.2300
                                                                 zoom:14.0
                                                              bearing:0
                                                         viewingAngle:0.00];
    self.mapView = [GMSMapView mapWithFrame:CGRectMake(0, 45, self.view.frame.size.width, self.view.frame.size.height - 45) camera:camera];
    mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

    mapView.delegate = self;
    mapView.myLocationEnabled = YES;
    mapView.mapType = kGMSTypeTerrain;
    mapView.settings.compassButton = YES;
    mapView.settings.myLocationButton = YES;
    [self.mapView setMinZoom:10 maxZoom:18];

    GMSMarker* marker = [[GMSMarker alloc] init];
    marker.position = CLLocationCoordinate2DMake(28.6100, 77.2300);
    marker.title = @"New Delhi";
    marker.snippet = @"Capital Of India";

    marker.map = self.mapView;
    marker.appearAnimation = kGMSMarkerAnimationPop;
    marker.icon = [GMSMarker markerImageWithColor:[UIColor grayColor]];
    [self.view addSubview:self.mapView];

如需进一步参考,请查看此PDF文档

您还可以根据需要设置最小和最大缩放级别:

[self.mapView setMinZoom:10 maxZoom:30];

希望这能解决问题。

0

// 我找到了适合我的方法

func setMapZoomToRadius(lat:Double, lng:Double, var mile:Double)
    {

        let center = CLLocationCoordinate2DMake(lat, lng)           
        let radius: Double = (mile ) * 621.371 

        let region = MKCoordinateRegionMakeWithDistance(center, radius * 2.0, radius * 2.0)


        let northEast = CLLocationCoordinate2DMake(region.center.latitude - region.span.latitudeDelta, region.center.longitude - region.span.longitudeDelta)
        let  southWest = CLLocationCoordinate2DMake(region.center.latitude + region.span.latitudeDelta, region.center.longitude + region.span.longitudeDelta)


        print("\(region.center.longitude)  \(region.span.longitudeDelta)")
        let bounds = GMSCoordinateBounds(coordinate: southWest, coordinate: northEast)

        let camera = googleMapView.cameraForBounds(bounds, insets:UIEdgeInsetsZero)
         googleMapView.camera = camera;

      }

0
截至2014年6月,此答案是迭代给定标记数组并相应设置边界的最简单方法。

0

我在框架的头文件中搜索,只找到了以下代码可以使用的接口,这可能是一个开始。问题在于我找不到任何其他头文件中GMSCoordinateBounds的导入,因此我找不到在GMSMapView中显示此区域的方法。

GMSVisibleRegion region;
region.farLeft = CLLocationCoordinate2DMake(farLeftLatitude, farLeftlongitude);
region.farRight = CLLocationCoordinate2DMake(farRightlatitude, farRightlongitude); 

GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithRegion:region];

但是如何设置这个区域呢? - Mecid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接