ios - 如何确定聚焦于 MKPolygon 的 MKMapCamera 的正确高度

标签 ios mapkit mkmaprect cllocationcoordinate2d mkmapsnapshotter

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

填写“区域”、“比例”、“大小”和“mapType”属性很简单,因为我有一个 MKPolygon 可以使用。棘手的部分是设置“相机”——在我的特定情况下,我正在独立于 MKMapView 使用 MKMapSnapshotter(事实上,甚至不在主线程上)。

但是,我更愿意调整快照的方向,使其适合基于非零航向的多边形边界——也就是说,我正在拍照的区域有一个“开始”和一个“end',我想从结果图像的底部到顶部定位。由于多边形基本上永远不会以 0 度航向自然定向,因此我需要确定“centerCoordinate”、“heading”和“altitude”。

因为我有多边形的坐标,所以我能够很容易地得出中心坐标和所需的航向——多边形的第一个坐标与形状的“开始”和结束(或其他坐标,在我的case) 与“结束”相关。

事实证明,确定海拔高度更加困难;我想确保多边形区域最终填充我希望显示的快照图像的纵横比。如何在不依赖 MKMapView 的“setRegion”选择器的情况下计算与 MKMapCamera 一起使用的正确高度?

最佳答案

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

1) 在确定边界矩形时围绕其中心坐标旋转 MKPolygon 以消除航向/旋转问题:询问 MKPolygon 是否为“boundingMapRect”,如果没有这个,将返回适合整个形状的任何最小矩形。如果一个又长又瘦的多边形恰好从东北向西南呈对角线方向,则边界矩形将接近正方形。执行旋转允许在确定其纵横比时考虑多边形的方向。

2) 将多边形的航向校正边界矩形调整为快照视口(viewport)的纵横比:这可确保非常“高”的多边形仍能正确适合广角视口(viewport),反之亦然。

3)[从我的示例代码中删除] 创建一个多边形的纵横比校正边界矩形并使用多边形的中心坐标将其旋转回原始航向:如果使用大区域,因为下一步涉及水平/垂直边界距离之间的测量。就我而言,我正在处理非常小的区域,这些区域不应受到地球曲率的足够影响而不会产生真正的影响。

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

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

在这一点上,我对如何在没有至少 1 个角的情况下求解所得三角形的高度感到有点困惑。在使用 MKMapView 实例执行一些测试时,MKMapCamera 的孔径似乎约为 30 度——这与增加视口(viewport)的纵横比、多边形的纵横比或任何其他方面无关除了地球曲率之外的其他因素。我对这个断言可能是错误的。

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

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

谢谢!

哦,当然还有代码:

+ (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];
}

关于ios - 如何确定聚焦于 MKPolygon 的 MKMapCamera 的正确高度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21034409/

相关文章:

ios - 使用自定义按钮从 Core Data UITableView 中删除一行

ios - 如何在 iOS 中打开文件?

ios - 为什么我的 UILabel 在其他所有内容之后延迟显示大约 10 秒?

swift - 如何使用 Kingfisher 在 map 注释 View 上设置图像?

javascript - Mapkit JS 无法在手机上移动 map

ios - 从坐标创建 MKMapRect 并使用 mapView.visibleMapRect

objective-c - 获取另一个注解周围的所有注解

ios - 基本注册页面 - iOS

ios - 如何在iOS8中使用MKMapView "Shows User Location"?

ios - Polyline drawMapRect 优化绘图