2010-10-08 3 views
15

ОБНОВЛЕНИЕ:Как отобразить изображение на MKOverlayView?

Изображение, которые проецируются на MKMapView с использованием MKOverlayView использовать Mercator projection, в то время как изображение, которое я использую в качестве входных данных использует проекцию WGS84. Есть ли способ конвертировать входное изображение в правый проектор WGS84 -> Mercator, без разбивки изображения и его можно сделать «на лету»?

Обычно вы можете преобразовать изображение в правый проектор, используя программу gdal2tiles. Входные данные, однако, изменяются каждые 15 минут, поэтому изображение должно быть преобразовано каждые 15 минут. Поэтому преобразование должно выполняться «на лету». Я также хочу, чтобы плитка выполнялась Mapkit, а не сама, используя gdal2tiles или структуру GDAL.

UPDATE END

В настоящее время я работаю над проектом, который отображает осадков радар над какой-то части мира. Радарное изображение предоставляется EUMETSAT, они предлагают файл KML, который можно загрузить в Google Earth или Google Maps. Если я загружаю KML-файл в Карты Google, он отображается отлично, но если я рисую изображение с помощью MKOverlayView на MKMapView, изображение немного.

Например, с левой стороны, Карты Google и справа, то же изображение отображается в MKMapView.

alt text

alt text

Поверхность крышки, что изображение можно просматривать на Google Maps, спутник, который используется для изображения является «Метеосат 0 градусов» спутник.

Поверхность, на которой оба изображения имеют одинаковый размер, это LatLonBox из файла KML, в котором указаны верхняя, нижняя, правая и левая стороны ограничивающего прямоугольника для наложения на землю.

<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> 

создать новый пользовательский MKOverlay объект называется RadarOverlay с этими параметрами,

[[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.

Похоже, что изображение растянулось, но оно выглядит в центре изображения, которое находится на экваторе.

+2

Я совершенно не в своей глубине здесь, но может ли эта проблема иметь какое-то отношение к тому, что, хотя широтное расстояние постоянное (~ 111 км/степени), то продольное расстояние изменяется как cos (phi) * R_e? –

+0

Это будет случай, если вы нарисуете изображение на сферическом объекте, но в соответствии с документацией; http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/LocationAwarenessPG/MapKit/MapKit.html все должно быть правильно нарисовано, потому что это плоская поверхность – Jeroen

+1

Я столкнулся с той же проблемой, просто с разными радарными данными. Карты Google отлично рисуют KML, но когда я рисую его с помощью MKOverlayRenderer, это немного, как лат, так и долго. – sethdeckard

ответ

0

Ваше изображение/наложение, скорее всего, более не пропорционально (то есть оно растянуто).Я видел подобное в своем приложении, где центр обзора верен, но когда вы уходите от экватора к полюсу (сверху и снизу экрана), карта/наложение все больше искажается.

Вы, очевидно, масштабируете свое изображение с помощью scaleFactor. Я бы посмотрел на это для начала.

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

Еще один хороший способ проверить свой код, чтобы увидеть, если масштабирование является виновником, чтобы масштабировать MKMapView до размера накладываемого изображения. Оставьте только оверлейное изображение, и если оно больше не искажено, вы знаете, что это проблема.

+0

Спасибо за ваш ответ, коэффициент масштабирования, который я использую для тестирования, всегда равен 1, поэтому он не будет масштабироваться мной, в этом случае я буду комментировать весь код масштабирования. Я не совсем понимаю, что вы подразумеваете под «заключается в масштабировании вашего MKMapView до размера наложенного изображения», само изображение наложения составляет 2048x2048 пикселей, но его нужно растянуть, см. Ссылку на Google Maps для этого. Он только растягивается не так. Как вы его решили? Я ценю вашу помощь. – Jeroen

+0

Либо сделайте mkmapview 2048x2048. Или масштабируйте изображение вниз, чтобы сказать 320x320. Тогда сделайте свой mkmapview 320x320. Я не уверен, почему изображение должно быть растянуто (я набираю это на iPod, поэтому я не могу сейчас следовать этой ссылке). – Andrew

+0

Изменение размера MKMapView не имеет никакого эффекта, MKMapView - это всего лишь прямоугольник, выложенный над картой мира, которая обрезается. Изменение размера изменяет только прямоугольник, но не влияет на карту мира или на любые аннотации или оверлеи, отображаемые на карте мира. Поверхность MKOverlayView кажется правильной и, по-видимому, соответствует поверхности наложения на Google Maps, разница в том, как она растягивается. – Jeroen

0

У Apple есть пример приложения из WWDC, который анализирует KML и отображает его на карте. Если вы платный разработчик, вы можете получить к нему доступ от WWDC videos page in iTunes. Я рекомендую использовать их парсер.

+1

Еще один парсер - синтаксический анализатор Simple-KML: http://github.com/incanus/Simple-KML –

+0

Разбор не проблема, проблема в том, как вы реализуете анализируемые данные на MKOverlayView – Jeroen

+0

И анализатор Apple заботится о что. –

1

Я не уверен, повлияет ли это на проблему масштабирования, но в вашем коде OverlayView вы рисуете все изображение для каждого maptile.

Вы пробовали только рисовать часть изображения, которое видно в mapRect?

Когда у меня были проблемы с MKOverlayViews, мне было полезно нарисовать прямоугольник наложения (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); 
} 
+0

Я уже реализовал это в своем коде, он фиксирует видимый прямоугольник, так что была нарисована только эта часть наложения. Я обновлю его в своем исходном коде. Спасибо за головы. – Jeroen

5

Вы уже видели «Session 127 - Настройка карт с накладками» от WWDC 2010 видео ? В одном из примеров приводятся данные землетрясений, которые дают землетрясение на площади 0,5 на 0,5 градуса и сопоставляют их. Ваши данные радара похожи, на основе квадратов. В образце кода есть полное приложение HazardMaps, которое берет эти данные и создает наложение с использованием MKMapPoints. Если вы еще не видели это видео, я думаю, это даст вам много полезной информации. Он также говорит о переходе на проекцию Меркатора.

Еще одна вещь, которую нужно проверить, - это какая система координат (данных) данных из EUMETSAT. В Google Maps используется система под названием WGS-84, которая является общим стандартом. Но есть много других стандартов, которые могут дать более точные позиции в разных частях мира. Если вы используете широту и долготу из другого стандарта в Картах Google, все ваши очки будут отключены на определенную сумму. Смещение не согласовано, оно изменяется при перемещении по карте. Вполне возможно, что Карты Google умны в отношении данных и конвертируются в WGS-84 «на лету».

Вы можете найти более подробную информацию, посмотрев на KML. Я посмотрел, но не смог найти финальный KML с прямоугольниками. Возможно, это дает информацию о том, какая система координат используется в метаданных.

+0

Спасибо за ваш ответ, я уже смотрел это видео, разница между этим проектом и проектом HazardMap заключается в том, что проект Hazardmap загружает файл, содержащий данные для каждого из конкретных прямоугольников, в то время как в моем проекте я загружаю один файл PNG, содержащий данные для всех. Файл kml, который я использую, можно найти здесь, http://oiswww.eumetsat.int/IPPS/html/GE/MET0D_VP-MPE.kml, может быть, вы можете взглянуть на него? Я думаю, что Google Maps преобразует изображение «на лету», вопрос только в том, как это сделать на iPhone? – Jeroen

+0

В этом видео он упоминает также проект под названием TileMap, в этом проекте изображение отображается правильно на карте, единственное, что изображение предварительно разбито. Когда я рисую изображение в оверлее на карте, он автоматически разбивается на iOS-систему. Я думаю, когда они создают изображение в проекте TileMap, оно преобразуется для использования проекции Меркатора. Они разбивают его по следующей программе: http://www.gdal.org/gdal2tiles.html – Jeroen

+0

Возможно, эта ссылка также может быть полезной, http: //wiki.osgeo.org/wiki/Tile_Map_Service_Specification – Jeroen

1

Я предполагаю, что Google Maps растягивает изображение нелинейно, чтобы компенсировать проекцию карты и что код вашего кода/Apple не является.

Одним из возможных решений было бы разделить изображение наложения на более мелкие прямоугольники и вызвать MKMapPointForCoordinate() отдельно для каждого прямоугольника. Тогда данные будут намного ближе к правильности.

1

Данные WGS-84 используют проекцию UTM, MKMapView использует меркатор. Вы используете разные методы для сопоставления 3D-объекта с 2D-поверхностью, поэтому он не дает одинаковых результатов.

Вам нужно будет спуститься в GDAL и переместить отображаемое изображение на другую проекцию. Дайте мне знать, если вы это поняли, потому что это нелегко.

Смежные вопросы