如何确定MKMapCamera专注于MKPolygon的正确高度

Dar*_* H. 6 mapkit ios mkmaprect cllocationcoordinate2d mkmapsnapshotter

我需要弄清楚如何设置MKMapSnapshotterOptions来拍摄与地球多边形区域相关的空中/卫星图像的快照.

填写'region','scale','size'和'mapType'属性是微不足道的,因为我有一个MKPolygon可以使用.棘手的部分是设置'相机' - 在我的特定情况下,我使用MKMapSnapshotter独立于MKMapView(实际上,甚至不在主线程上).

但是,我更倾向于定位快照,使其符合基于非零标题的多边形边界 - 也就是说,我正在拍摄的区域具有'开始'和'结束',我想从结果图像的底部到顶部定向.由于多边形基本上永远不会在0度标题上自然定向,因此我需要确定'centerCoordinate','heading'和'altitude'.

由于我有多边形的坐标,我能够相当容易地导出中心坐标和所需的标题 - 多边形的第一个坐标与形状的"开始"和结束(或者我的情况下的其他坐标)相关联到最后'.

确定海拔高度证明更加困难; 我想确保多边形区域最终填满我想要显示的快照图像的宽高比.如何在不依赖MKMapView的'setRegion'选择器的情况下计算与MKMapCamera一起使用的正确高度?

Dar*_* H. 19

为了解决这个问题,我最终做了以下事情:

1)在确定边界矩形时围绕它的中心坐标旋转MKPolygon以消除标题/旋转问题:向MKPolygon询问它的'boundingMapRect',如果没有这将返回整个形状周围的任何最小矩形.如果一个长而瘦的多边形碰巧从东北向西南方向倾斜,则边界矩形几乎是方形的.执行旋转允许在确定多边形的纵横比时考虑多边形的标题.

2)将多边形的经过标题校正的边界矩形拟合到快照视口的纵横比中:这可确保非常"高"的多边形仍然可以在宽视角视口中正确拟合,反之亦然.

3)[从我的示例代码中删除]创建生成的经方面校正的边界矩形的多边形,并使用多边形的中心坐标将其旋转回原始标题:如果处理大区域,可能需要这样做,因为下一步涉及水平/垂直边界距离之间的测量.在我的情况下,我正在与非常小的区域一起工作,这些区域不应受到地球曲率的影响,以产生真正的差异.

4)确定以米为单位的总水平和垂直边界区域

5)使用两个距离的较大尺寸(尺寸)来形成三角形的基础测量,其中A =轴上的最小坐标位置,B =轴上的最大坐标位置,C =相机位置(多边形的中心坐标) )

在这一点上,我有点难以理解如何在没有至少一个角度的情况下求解所得三角形的高度.在使用MKMapView实例执行某些测试时,看起来MKMapCamera的光圈大约是30度 - 这无论是增加视口的纵横比,多边形的纵横比,还是除了曲率之外的任何其他因素.地球.我对这个断言可能是错的.

5)使用我的测试中观察到的孔径角,使用(尺寸/ 2)/ tan(aperture_angle_in_radians/2)计算所需的高度

看到我花了多少时间花在这上面,我决定在StackOverflow上发布问题/答案组合希望它:1)帮助处于相同情况的其他人2)通过比我更聪明的方式纠正并导致更好的解决方案

谢谢!

OH,当然还有代码:

+ (double)determineAltitudeForPolygon:(MKPolygon *)polygon withHeading:(double)heading andWithViewport:(CGSize)viewport {
    // Get a bounding rectangle that encompasses the polygon and represents its
    // true aspect ratio based on the understanding of its heading.
    MKMapRect boundingRect = [[self rotatePolygon:polygon withCenter:MKMapPointForCoordinate(polygon.coordinate) byHeading:heading] boundingMapRect];

    MKCoordinateRegion boundingRectRegion = MKCoordinateRegionForMapRect(boundingRect);

    // Calculate a new bounding rectangle that is corrected for the aspect ratio
    // of the viewport/camera -- this will be needed to ensure the resulting
    // altitude actually fits the polygon in view for the observer.
    CLLocationCoordinate2D upperLeftCoord = CLLocationCoordinate2DMake(boundingRectRegion.center.latitude + boundingRectRegion.span.latitudeDelta / 2, boundingRectRegion.center.longitude - boundingRectRegion.span.longitudeDelta / 2);
    CLLocationCoordinate2D upperRightCoord = CLLocationCoordinate2DMake(boundingRectRegion.center.latitude + boundingRectRegion.span.latitudeDelta / 2, boundingRectRegion.center.longitude + boundingRectRegion.span.longitudeDelta / 2);
    CLLocationCoordinate2D lowerLeftCoord = CLLocationCoordinate2DMake(boundingRectRegion.center.latitude - boundingRectRegion.span.latitudeDelta / 2, boundingRectRegion.center.longitude - boundingRectRegion.span.longitudeDelta / 2);

    CLLocationDistance hDist = MKMetersBetweenMapPoints(MKMapPointForCoordinate(upperLeftCoord), MKMapPointForCoordinate(upperRightCoord));
    CLLocationDistance vDist = MKMetersBetweenMapPoints(MKMapPointForCoordinate(upperLeftCoord), MKMapPointForCoordinate(lowerLeftCoord));

    double adjacent;
    double newHDist, newVDist;

    if (boundingRect.size.height > boundingRect.size.width) {
        newVDist = vDist;
        newHDist = (viewport.width / viewport.height) * vDist;

        adjacent = vDist / 2;
    } else {
        newVDist = (viewport.height / viewport.width) * hDist;
        newHDist = hDist;

        adjacent = hDist / 2;
    }

    double result = adjacent / tan(Deg_to_Rad(15));
    return result;
}

+ (MKPolygon *)rotatePolygon:(MKPolygon *)polygon withCenter:(MKMapPoint)centerPoint byHeading:(double)heading {
    MKMapPoint points[polygon.pointCount];
    double rotation_angle = -Deg_to_Rad(heading);

    for(int i = 0; i < polygon.pointCount; i++) {
        MKMapPoint point = polygon.points[i];

        // Translate each point by the coordinate to rotate around, use matrix
        // algebra to perform the rotation, then translate back into the
        // original coordinate space.
        double newX = ((point.x - centerPoint.x) * cos(rotation_angle)) + ((centerPoint.y - point.y) * sin(rotation_angle)) + centerPoint.x;
        double newY = ((point.x - centerPoint.x) * sin(rotation_angle)) - ((centerPoint.y - point.y) * cos(rotation_angle)) + centerPoint.y;

        point.x = newX;
        point.y = newY;

        points[i] = point;
    }

    return [MKPolygon polygonWithPoints:points count:polygon.pointCount];
}
Run Code Online (Sandbox Code Playgroud)