MKOverlayRenderer加载NOAA雷达图像显示异常(拉伸)求助
我尝试通过自定义MKOverlay与MKOverlayRenderer子类,在MapView中展示NOAA雷达图像。尝试多种方法后,要么因mapRect不正确导致无图像显示,要么图像南北方向拉伸至加拿大和南美区域,东西方向显示正常。怀疑是渲染器中mapRect的问题,已参考多个相关链接仍未解决,附上相关调用方法及代码:
调用显示的方法
func loadRadarImage() { let center = CLLocationCoordinate2D(latitude: 47.6062, longitude: -122.3320) let overlay = RadarOverlay(coord: center, rect: self.mapView.visibleMapRect) self.mapView.addOverlay(overlay, level: .aboveRoads) }
MKOverlay类
class RadarOverlay: NSObject, MKOverlay { var coordinate: CLLocationCoordinate2D var boundingMapRect: MKMapRect init(coord: CLLocationCoordinate2D, rect: MKMapRect) { self.coordinate = coord self.boundingMapRect = rect } }
MKOverlayRenderer类
@objcMembers class NOAAMapOverlayRenderer : MKOverlayRenderer { var radarImage : UIImage? init(overlay: MKOverlay, overlayImage: UIImage) { self.radarImage = overlayImage super.init(overlay: overlay) } override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) { if let mapImage = self.radarImage?.cgImage { let mapRect = rect(for: overlay.boundingMapRect) // CGRect context.scaleBy(x: 1.0, y: -1.0) context.translateBy(x: 0.0, y: CGFloat(-mapRect.size.height)) context.draw(mapImage, in: mapRect) } } }
当前效果:图像南北方向过度拉伸,东西方向显示正常。
咱们来一步步排查修复这个问题,核心问题出在地理范围的定义和渲染时的坐标系/比例匹配上,下面是具体的调整方案:
1. 给Overlay设置正确的雷达地理范围
你现在直接用地图的可视区域visibleMapRect作为Overlay的边界,但NOAA雷达图本身有固定的经纬度覆盖范围,不能跟着当前地图视图走。你需要先确定雷达图对应的实际地理边界,再转换成正确的MKMapRect。
比如假设你的雷达图覆盖北纬45°到49°、西经120°到124°,可以这样计算:
// 定义雷达图的四个边角坐标 let topLeftCoord = CLLocationCoordinate2D(latitude: 49.0, longitude: -124.0) let bottomRightCoord = CLLocationCoordinate2D(latitude: 45.0, longitude: -120.0) // 转换为MapKit的地图点 let topLeftPoint = MKMapPoint(topLeftCoord) let bottomRightPoint = MKMapPoint(bottomRightCoord) // 计算准确的boundingMapRect let radarMapRect = MKMapRect( x: topLeftPoint.x, y: topLeftPoint.y, width: bottomRightPoint.x - topLeftPoint.x, height: bottomRightPoint.y - topLeftPoint.y )
然后用这个radarMapRect初始化RadarOverlay,替换原来的mapView.visibleMapRect。
2. 修复Renderer的绘制逻辑
你当前的draw方法有两个问题:一是没考虑zoomScale的影响,二是坐标系翻转的顺序不对。修改后的代码如下:
override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) { guard let radarCGImage = radarImage?.cgImage else { return } // 获取Overlay地理范围对应的屏幕矩形(自动适配当前缩放级别) let overlayScreenRect = rect(for: overlay.boundingMapRect) // 保存上下文状态,避免干扰其他绘制操作 context.saveGState() // 正确翻转y轴:Core Graphics原点在左下角,MapKit在左上角 context.translateBy(x: 0, y: overlayScreenRect.height) context.scaleBy(x: 1, y: -1) // 把雷达图绘制到正确的屏幕区域 context.draw(radarCGImage, in: overlayScreenRect) // 恢复上下文状态 context.restoreGState() }
3. 确保图像比例和地理范围比例一致
雷达图是固定宽高比的,如果你的MKMapRect宽高比和图像不一致,必然会拉伸。可以加个验证步骤:
let imageRatio = radarImage.size.width / radarImage.size.height let mapRectRatio = radarMapRect.width / radarMapRect.height // 允许微小误差,避免浮点精度问题 assert(abs(imageRatio - mapRectRatio) < 0.01, "雷达图比例和地理范围比例不匹配,会导致拉伸!")
如果比例不匹配,需要调整地理范围的计算,或者对图像进行裁剪/缩放预处理。
4. 调整Overlay的添加时机
不要在地图还没加载完成时就调用loadRadarImage,否则visibleMapRect可能不准确。建议在mapView(_:regionDidChangeAnimated:)代理方法中更新Overlay,或者等地图初始化完成后再加载。
内容的提问来源于stack exchange,提问作者Tim

