为了解决这个问题,我最终做了以下事情:
1)在确定边界矩形时围绕其中心坐标旋转 MKPolygon 以消除航向/旋转问题:如果没有这个,向 MKPolygon 询问它的“boundingMapRect”将返回适合整个形状的任何最小矩形。如果一个细长的多边形恰好是从东北到西南的对角线,则边界矩形将接近正方形。执行旋转允许在确定多边形的纵横比时考虑多边形的方向。
2)将多边形的航向校正边界矩形拟合到快照视口的纵横比中:这确保了一个非常“高”的多边形仍然可以正确地适合宽纵横比的视口,反之亦然。
3)[从我的示例代码中删除]创建一个多边形,并使用多边形的中心坐标将其旋转回原始标题:如果处理大区域,则可能需要这样做,因为下一步涉及水平/垂直边界距离之间的测量。就我而言,我正在处理非常小的区域,这些区域不应受到地球曲率的足够影响以产生真正的影响。
4) 确定以米为单位的总水平和垂直边界区域
5)使用两个距离的较大的维度(Dimension)来形成一个三角形的基础测量,其中A = 轴上的最小坐标位置,B = 轴上的最大坐标位置,C = 相机位置(多边形的中心坐标) )
在这一点上,我对于如何在没有至少 1 个角的情况下求解所得三角形的高度感到有些困惑。在使用 MKMapView 实例执行一些测试时,看起来 MKMapCamera 的孔径约为 30 度——这与增加视口的纵横比、多边形的纵横比或除曲率之外的任何其他因素无关。地球。我可能对这个断言是错误的。
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];
}