如何在MKOverlayView上显示图像?

时间:2010-10-08 15:07:18

标签: iphone objective-c cocoa-touch core-graphics mapkit

更新:

使用MKOverlayView投影在MKMapView上的图像使用Mercator projection,而我用作输入数据的图像使用WGS84投影。有没有办法将输入图像转换为正确的投影WGS84 - >墨卡托,没有平铺图像,可以在飞行中完成吗?

通常,您可以使用程序gdal2tiles将图像转换为正确投影。 然而,输入数据每十五分钟改变一次,因此图像必须每十五分钟转换一次。所以转换必须在飞行中完成。我也希望平铺由Mapkit完成,而不是我自己使用gdal2tiles或GDAL框架。

更新结束

我目前正在开展一个项目,在世界某些地方展示降雨雷达。雷达图像由EUMETSAT提供,它们提供可以加载到Google Earth或Google Maps的KML文件。如果我在Google地图中加载KML文件,它会完美显示,但如果我在MKMapView上使用MKOverlayView绘制图像,则图像会略微显示。

例如,在左侧,Google地图和右侧,相同的图像显示在MKMapView上。

alt text

alt text

可以在Google Maps上查看图像覆盖的表面,用于图像的卫星是“Meteosat 0 Degree”卫星。

两个图像覆盖的表面大小相同,这是KML文件中的LatLonBox,它指定了地面叠加层边界框的顶部,底部,右侧和左侧的对齐位置。

  <LatLonBox id="GE_MET0D_VP-MPE-latlonbox">
        <north>57.4922</north>
        <south>-57.4922</south>
        <east>57.4922</east>
        <west>-57.4922</west>
        <rotation>0</rotation>
  </LatLonBox>

我使用这些参数

创建一个名为RadarOverlay的新自定义MKOverlay对象
[[RadarOverlay alloc] initWithImageData:[[self.currentRadarData objectAtIndex:0] valueForKey:@"Image"] withLowerLeftCoordinate:CLLocationCoordinate2DMake(-57.4922, -57.4922) withUpperRightCoordinate:CLLocationCoordinate2DMake(57.4922, 57.4922)];

自定义MKOverlay对象的实现; RadarOverlay

- (id) initWithImageData:(NSData*) imageData withLowerLeftCoordinate:(CLLocationCoordinate2D)lowerLeftCoordinate withUpperRightCoordinate:(CLLocationCoordinate2D)upperRightCoordinate
{
     self.radarData = imageData;

     MKMapPoint lowerLeft = MKMapPointForCoordinate(lowerLeftCoordinate);
     MKMapPoint upperRight = MKMapPointForCoordinate(upperRightCoordinate);

     mapRect = MKMapRectMake(lowerLeft.x, upperRight.y, upperRight.x - lowerLeft.x, lowerLeft.y - upperRight.y);

     return self;
}

- (CLLocationCoordinate2D)coordinate
{
     return MKCoordinateForMapPoint(MKMapPointMake(MKMapRectGetMidX(mapRect), MKMapRectGetMidY(mapRect)));
}

- (MKMapRect)boundingMapRect
{
     return mapRect;
}

自定义MKOverlayView,RadarOverlayView

的实现
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context
{
    RadarOverlay* radarOverlay = (RadarOverlay*) self.overlay;

    UIImage *image          = [[UIImage alloc] initWithData:radarOverlay.radarData];

    CGImageRef imageReference = image.CGImage;

    MKMapRect theMapRect    = [self.overlay boundingMapRect];
   CGRect theRect           = [self rectForMapRect:theMapRect];
    CGRect clipRect     = [self rectForMapRect:mapRect];

    NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
    CGContextSetAlpha(context, [preferences floatForKey:@"RadarTransparency"]);

    CGContextAddRect(context, clipRect);
    CGContextClip(context);

    CGContextDrawImage(context, theRect, imageReference);

    [image release]; 
}

当我下载图像时,我翻转图像以便可以在MKOverlayView中轻松绘制

size_t width    = (CGImageGetWidth(imageReference) / self.scaleFactor);
size_t height   = (CGImageGetHeight(imageReference) / self.scaleFactor);

// Calculate colorspace for the specified image
CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageReference);

// Allocate and clear memory for the data of the image
unsigned char *imageData = (unsigned char*) malloc(height * width * 4);
memset(imageData, 0, height * width * 4);

// Define the rect for the image
CGRect imageRect;
if(image.imageOrientation==UIImageOrientationUp || image.imageOrientation==UIImageOrientationDown) 
    imageRect = CGRectMake(0, 0, width, height); 
else 
    imageRect = CGRectMake(0, 0, height, width); 

// Create the imagecontext by defining the colorspace and the address of the location to store the data
CGContextRef imageContext = CGBitmapContextCreate(imageData, width, height, 8, width * 4, imageColorSpace, kCGImageAlphaPremultipliedLast);

CGContextSaveGState(imageContext);

// Scale the image to the opposite orientation so it can be easylier drawn with CGContectDrawImage
CGContextTranslateCTM(imageContext, 0, height);
CGContextScaleCTM(imageContext, 1.0, -1.0);

if(image.imageOrientation==UIImageOrientationLeft) 
{
    CGContextRotateCTM(imageContext, M_PI / 2);
    CGContextTranslateCTM(imageContext, 0, -width);
}
else if(image.imageOrientation==UIImageOrientationRight) 
{
    CGContextRotateCTM(imageContext, - M_PI / 2);
    CGContextTranslateCTM(imageContext, -height, 0);
} 
else if(image.imageOrientation==UIImageOrientationDown) 
{
    CGContextTranslateCTM(imageContext, width, height);
    CGContextRotateCTM(imageContext, M_PI);
}

// Draw the image in the context
CGContextDrawImage(imageContext, imageRect, imageReference);
CGContextRestoreGState(imageContext);

在我翻转图像后,我操纵它,然后将其作为NSData对象存储在内存中。

看起来图像被拉伸了,但它看起来就像是在赤道处的图像中心。

6 个答案:

答案 0 :(得分:5)

您是否已经看过WWDC 2010视频中的“会话127 - 使用叠加层自定义地图”?其中一个例子是地震数据,它给出0.5到0.5度区域的地震风险并对它们进行映射。基于正方形,您的雷达数据看起来很相似。示例代码有一个名为HazardMaps的完整应用程序,它使用MKMapPoints获取此数据并创建叠加层。如果你还没有看过这个视频,我想它会给你很多有用的信息。他还谈到转换到墨卡托的预测。

要检查的另一件事是来自EUMETSAT的数据的坐标系(数据)是什么.Google Maps使用名为WGS-84的系统,这是一般标准。但是还有许多其他标准可以在世界不同地区提供更准确的位置。如果您使用谷歌地图中不同标准的纬度和经度,您的所有积分将会减少一定数量。偏移量不一致,当您在地图上移动时,它会发生变化。 Google地图可能会对数据进行智能处理并即时转换为WGS-84。

您可以通过查看KML了解更多详情。我看了,但找不到最终的KML,矩形。也许它提供了有关它在元数据中使用的坐标系的信息。

答案 1 :(得分:1)

我不确定这是否会影响缩放问题,但在OverlayView代码中,您为每个maptile绘制整个图像。

您是否尝试仅绘制mapRect中可见的图像部分?

当我遇到MKOverlayViews的问题时,它有助于我绘制叠加的rect(self.overlay.boundingRect)和mapRect(传递给drawMapRect)。虽然,我不确定绘制mapRect是否对您的情况有所帮助。

无论如何,继续使用我用来绘制矩形的函数,以防你想要试试

-(void)drawRect:(MKMapRect)rect inContext:(CGContextRef)context withLineWidth:(float)lineWidth andColor:(CGColorRef)color
{

    double maxx = MKMapRectGetMaxX(rect);
    double minx = MKMapRectGetMinX(rect);
    double maxy = MKMapRectGetMaxY(rect);
    double miny = MKMapRectGetMinY(rect);

    CGPoint tr = [self pointForMapPoint:(MKMapPoint) {maxx, maxy}];
    CGPoint br = [self pointForMapPoint:(MKMapPoint) {maxx, miny}];
    CGPoint bl = [self pointForMapPoint:(MKMapPoint) {minx, miny}];
    CGPoint tl = [self pointForMapPoint:(MKMapPoint) {minx, maxy}];

    CGMutablePathRef cgPath = CGPathCreateMutable();
    CGPathMoveToPoint(cgPath, NULL, tr.x, tr.y);
    CGPathAddLineToPoint(cgPath, NULL, br.x, br.y);
    CGPathAddLineToPoint(cgPath, NULL, bl.x, bl.y);
    CGPathAddLineToPoint(cgPath, NULL, tl.x, tl.y);
    CGPathAddLineToPoint(cgPath, NULL, tr.x, tr.y); 

    CGContextSaveGState(context);
    CGContextAddPath(context, cgPath);
    CGContextSetStrokeColorWithColor(context, color);
    CGContextSetLineJoin(context, kCGLineJoinRound);
    CGContextSetLineCap(context, kCGLineCapRound);
    CGContextSetLineWidth(context, lineWidth);
    CGContextStrokePath(context);
    CGPathRelease(cgPath);  
    CGContextRestoreGState(context);
}

答案 2 :(得分:1)

我的猜测是谷歌地图非线性地拉伸图像以补偿地图投影,而你的代码/ Apple的代码则没有。

一种可能的解决方案是将叠加图像细分为较小的矩形,并为每个矩形分别调用MKMapPointForCoordinate()。然后数据将更加接近正确。

答案 3 :(得分:1)

WGS-84数据使用UTM投影,MKMapView使用的是mercator。您正在使用不同的方法将3D对象映射到2D表面,因此它不会给出相同的结果。

您需要在GDAL中向下移动并将插入图像移动到不同的投影。如果你弄清楚了,请告诉我,因为这不是一件容易的事。

答案 4 :(得分:0)

您的图像/叠加层很可能不再成比例(即它已被拉伸)。我在自己的应用程序中看到过这种情况,其中视图的中心是正确的,但是当你离开赤道朝向任一极点(屏幕的顶部和底部)时,地图/覆盖变得越来越扭曲。

您显然是通过scaleFactor缩放图像。我会为初学者看一下。

size_t width    = (CGImageGetWidth(imageReference) / self.scaleFactor);
size_t height   = (CGImageGetHeight(imageReference) / self.scaleFactor);

测试代码以查看缩放是否是罪魁祸首的另一个好方法是将MKMapView缩小到叠加图像的大小。保留叠加图像,如果它不再失真,那么你知道这就是问题所在。

答案 5 :(得分:0)

Apple有一个来自WWDC的示例应用程序,它解析KML并将其显示在地图上。如果您是付费开发者,可以从WWDC videos page in iTunes访问它。我建议使用他们的解析器。