2010-10-08 28 views
15

ACTUALIZACIÓN:¿Cómo mostrar una imagen en un MKOverlayView?

imágenes que se proyectan en la MKMapView utilizando un MKOverlayView utilizar el Mercator projection, mientras que la imagen que utilizo como datos de entrada utiliza una proyección WGS84. ¿Hay alguna manera de convertir la imagen de entrada, a la proyección correcta WGS84 -> Mercator, sin instalar la imagen y puede hacerlo sobre la marcha?

Normalmente, puede convertir una imagen a la proyección derecha con el programa gdal2tiles. Sin embargo, los datos de entrada cambian cada quince minutos, por lo que la imagen debe convertirse cada quince minutos. Entonces la conversión tiene que hacerse sobre la marcha. También quiero que el mosaico sea hecho por Mapkit y no por mí mismo usando gdal2tiles o el framework GDAL.

ACTUALIZACIÓN FIN

Actualmente estoy trabajando en un proyecto que muestra un radar de precipitación sobre alguna parte del mundo. La imagen del radar es proporcionada por EUMETSAT, ofrecen un archivo KML que se puede cargar en Google Earth o Google Maps. Si cargo el archivo KML en Google Maps, se muestra perfectamente, pero si dibujo la imagen con MKOverlayView en un MKMapView, la imagen es un poco.

Por ejemplo, en el lado izquierdo, Google Maps y en el lado derecho, la misma imagen se muestra en un MKMapView.

alt text

alt text

La superficie que las cubiertas de imagen se pueden ver en Google Maps, el satélite que se utiliza para la imagen es el satélite "Meteosat 0 Grado".

La superficie que ambas imágenes cubren es del mismo tamaño, esta es la LatLonBox del archivo KML, especifica dónde se alinean los lados superior, inferior, derecho e izquierdo de un recuadro delimitador para la superposición de suelo.

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

creo un nuevo encargo MKOverlay objeto llamado RadarOverlay con estos parámetros,

[[RadarOverlay alloc] initWithImageData:[[self.currentRadarData objectAtIndex:0] valueForKey:@"Image"] withLowerLeftCoordinate:CLLocationCoordinate2DMake(-57.4922, -57.4922) withUpperRightCoordinate:CLLocationCoordinate2DMake(57.4922, 57.4922)]; 

La implementación de la costumbre MKOverlay objeto; 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; 
} 

La implementación de la costumbre 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]; 
} 

Al descargar la imagen, me tapa la imagen para que pueda ser fácilmente dibuja en el 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); 

Después Volteé la imagen, la manipulo y luego la almaceno en la memoria como un objeto NSData.

Parece que la imagen se estiró, pero se ve bien en el centro de la imagen, que está en el ecuador.

+2

Estoy completamente fuera de mi alcance aquí, pero podría este problema tener algo que ver con el hecho de que mientras la distancia latitudinal es constante (~ 111km/grado) en todas partes, la distancia longitudinal varía como cos (phi) * R_e? –

+0

Este sería el caso si dibuja la imagen en un objeto esférico, pero de acuerdo con la documentación; http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/LocationAwarenessPG/MapKit/MapKit.html todo se debe dibujar correctamente porque es una superficie plana – Jeroen

+1

Me estoy encontrando con el mismo problema, solo con diferentes datos de radar. Google Maps está dibujando el archivo KML a la perfección, pero cuando lo dibujo con un MKOverlayRenderer no funciona, tanto en lat como en long. – sethdeckard

Respuesta

0

Su imagen/superposición probablemente ya no es proporcional (es decir, se ha estirado).He visto este tipo de cosas en mi propia aplicación, donde el centro de la vista es correcto, pero a medida que te alejas del ecuador hacia cualquiera de los polos (arriba y abajo de la pantalla) el mapa/superposición se distorsiona cada vez más.

Obviamente, está escalando su imagen por scaleFactor. Echaré un vistazo a eso para empezar.

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

Otra buena manera de probar el código para ver si hay escalado es el culpable, es ampliar su MKMapView hasta el tamaño de la imagen superpuesta. Deje la imagen superpuesta sola, y si ya no está distorsionada, entonces sabrá que ese es el problema.

+0

Gracias por su respuesta, el factor de escala que utilizo para las pruebas siempre es 1, por lo que no lo escalaré, en este caso comentaré todo el código de escala. No entiendo exactamente lo que quiere decir con "es escalar su MKMapView hasta el tamaño de la imagen de superposición", la imagen superpuesta en sí misma es de 2048x2048 píxeles, pero necesita estirarse, vea el enlace de Google Maps para eso. Solo se estira de la manera incorrecta. ¿Cómo lo resolvió? Aprecio tu ayuda. – Jeroen

+0

Haga el mkmapview 2048x2048. O escale la imagen hacia abajo para decir 320x320. Luego haz tu mkmapview 320x320. No estoy seguro de por qué la imagen tiene que estirarse (estoy escribiendo esto en un iPod, así que no puedo seguir ese enlace en este momento). – Andrew

+0

Cambiar el tamaño de MKMapView no tiene ningún efecto, el MKMapView es solo un rectángulo dispuesto sobre el mapa mundial que se recorta. Cambiar el tamaño solo cambiará el rectángulo, pero no tiene efecto en el mapa del mundo en sí o en las anotaciones o superposiciones que se muestran en el mapa mundial. La superficie de MKOverlayView parece correcta y parece corresponderse con la superficie de la superposición en Google Maps, la única diferencia es la forma en que se estira. – Jeroen

0

Apple tiene una aplicación de muestra de WWDC que analiza KML y lo muestra en un mapa. Si eres un desarrollador pago, puedes acceder al mismo desde WWDC videos page in iTunes. Recomiendo usar su analizador.

+1

Otro analizador es el analizador Simple-KML: http://github.com/incanus/Simple-KML –

+0

El análisis no es el problema, el problema es cómo implementar los datos analizados en un MKOverlayView – Jeroen

+0

Y el analizador de Apple se ocupa de ese. –

1

No estoy seguro de si esto afectará el problema de escala, pero en su código OverlayView, dibujará la imagen completa para cada objeto.

¿Ha intentado dibujar solo la parte de la imagen que está visible en mapRect?

Cuando he tenido problemas con MKOverlayViews, ha sido útil para mí dibujar el rect de la superposición (self.overlay.boundingRect) y el mapRect (pasado a drawMapRect). Aunque, no estoy seguro si dibujar el mapRect sería útil en su situación.

En cualquier caso, aquí está la función que utilizo para dibujar el rect en caso de que quiera probarlo

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

Ya he implementado eso en mi código, recorta el rect visible, por lo que solo se dibuja esa parte de la superposición. Lo actualizaré en mi código fuente. Gracias por el aviso. – Jeroen

5

¿Ya ha visto "Sesión 127 - Personalización de mapas con superposiciones" de la WWDC vídeos 2010 ? Uno de los ejemplos toma los datos del terremoto, que da el riesgo de terremoto para áreas de 0.5 por 0.5 grados y los mapea. Sus datos de radar son similares, basados ​​en cuadrados. El código de muestra tiene una aplicación completa llamada HazardMaps, que toma estos datos y crea una superposición usando MKMapPoints. Si aún no has visto este video, creo que te dará mucha información útil. También habla sobre la conversión a la proyección de Mercator.

Otra cosa para comprobar es en qué sistema de coordenadas (datum) están los datos de EUMETSAT. Google Maps usa un sistema llamado WGS-84, que es un estándar general. Pero hay muchos otros estándares que pueden dar posiciones más precisas en diferentes partes del mundo. Si usa la latitud y la longitud de un estándar diferente en Google Maps, todos sus puntos estarán desactivados en una cierta cantidad. El desplazamiento no es uniforme, cambia conforme se mueve alrededor del mapa. Es posible que Google Maps esté siendo inteligente con respecto a los datos y la conversión a WGS-84 sobre la marcha.

Puede encontrar más detalles mirando el archivo KML. Miré pero no pude encontrar el KML final, con los rectángulos. Tal vez brinde información sobre qué sistema de coordenadas está usando en los metadatos.

+0

Gracias por su respuesta, ya vi ese video, la diferencia entre este proyecto y el proyecto HazardMap es que el proyecto Hazardmap descarga un archivo que contiene datos para cada uno de los rectángulos específicos, mientras que en mi proyecto descargo un archivo PNG que contiene los datos para todos. El archivo kml que utilizo se puede encontrar aquí, http://oiswww.eumetsat.int/IPPS/html/GE/MET0D_VP-MPE.kml, ¿quizás puede echarle un vistazo? Creo que Google Maps convierte la imagen sobre la marcha, la única pregunta es, ¿cómo se haría esto en el iPhone? – Jeroen

+0

En ese video menciona también un proyecto llamado TileMap, en ese proyecto la imagen se muestra correctamente en el mapa, lo único es que la imagen se muestra de antemano. Cuando dibujo una imagen en una superposición en el mapa, el sistema de iOS la configura automáticamente. Creo que cuando crean la imagen en el proyecto TileMap, se convierte para usar la proyección de Mercator. Lo preparan usando el siguiente programa, http://www.gdal.org/gdal2tiles.html – Jeroen

+0

Quizás este enlace también puede ser de utilidad, http: //wiki.osgeo.org/wiki/Tile_Map_Service_Specification – Jeroen

1

Supongo que Google Maps está estirando la imagen de forma no lineal para compensar la proyección del mapa y que su código/código de Apple no lo está.

Una posible solución sería subdividir la imagen de superposición en rectángulos más pequeños y llamar a MKMapPointForCoordinate() por separado para cada rectángulo. Entonces los datos estarán mucho más cerca de ser correctos.

1

Los datos WGS-84 usan proyección UTM, MKMapView usa mercator. Está utilizando diferentes métodos para asignar un objeto 3D a una superficie 2D, por lo tanto, no está dando los mismos resultados.

Tendrá que bajar en GDAL y mover la imagen entrante a una proyección diferente. Avísame si lo descubriste, porque no es fácil