2011-07-18 4 views
5

J'utilise un MKOverlay personnalisé pour dessiner des données météorologiques sur un MKMapView. Le dessin est en cours dans CoreGraphics. Pour ce cas particulier, il n'est pas suffisant de faire le dessin dans la méthode drawMapRect: zoomScale: inContext: à cause de la façon dont il gère le pavage. J'ai besoin que l'image entière soit dessinée à la fois au lieu de mosaïque comme la méthode drawMapRect. Avant, j'avais l'image radar dans un fichier .gif, donc je viens de lui ajouter une imageView et de redimensionner l'imageView dans drawMapRect.Dessin sur mesure MKOverlay

Mon plan était de faire quelque chose de similaire avec ça. Ajoutez un UIView personnalisé et appelez setNeedsDisplay dans drawMapRect.

Voici le code approprié.

La propriété boundingMapRect de l'objet MKOverlay:

- (MKMapRect)boundingMapRect 
{ 
    CLLocationCoordinate2D upperLeftCoord = 
    CLLocationCoordinate2DMake(weatherData.radarArray.connectedRadar.latitude + 2.5, 
          weatherData.radarArray.connectedRadar.longitude - 2.5); 

    MKMapPoint upperLeft = MKMapPointForCoordinate(upperLeftCoord); 

    CLLocationCoordinate2D lowerRightCoord = 
    CLLocationCoordinate2DMake(weatherData.radarArray.connectedRadar.latitude - 2.5, 
          weatherData.radarArray.connectedRadar.longitude + 2.5); 

    MKMapPoint lowerRight = MKMapPointForCoordinate(lowerRightCoord); 

    double width = lowerRight.x - upperLeft.x; 
    double height = lowerRight.y - upperLeft.y; 

    MKMapRect bounds = MKMapRectMake(upperLeft.x, upperLeft.y, width, height); 

    return bounds; 
} 

Le drawMapRect de travail: zoomScale: inContext: code (qui est trop lent).

- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context { 

    int numPaths = parser.dataPaths.size(); 

    // We have to pad the map rect a lot to allow for visibility testing that works well. 
    MKMapRect testMapRect = MKMapRectMake(mapRect.origin.x - 40000, mapRect.origin.y - 40000, mapRect.size.width + 40000, mapRect.size.height + 40000);; 

    // Only draw inside the area we are suppose to 
    //CGRect rect = [self rectForMapRect:mapRect]; 
    //CGContextClipToRect(context, rect); 

    // How see through is the radar data. 1 = opaque, 0 = completely transparent 
    CGContextSetAlpha(context, 1); 
    for (int i = 0; i < numPaths; i++) { 
     // Make sure the bin is actually visible in this region before drawing it 
     if (MKMapRectContainsPoint(testMapRect, parser.dataPaths[i]->points[0]) || 
      MKMapRectContainsPoint(testMapRect, parser.dataPaths[i]->points[1]) || 
      MKMapRectContainsPoint(testMapRect, parser.dataPaths[i]->points[2]) || 
      MKMapRectContainsPoint(testMapRect, parser.dataPaths[i]->points[3])) { 
      CGMutablePathRef path = CGPathCreateMutable(); 
      CGPoint currentP = [self pointForMapPoint:parser.dataPaths[i]->points[0]]; 
      CGContextBeginPath(context); 
      CGPathMoveToPoint(path, NULL, currentP.x, currentP.y); 
      currentP = [self pointForMapPoint:parser.dataPaths[i]->points[1]]; 
      CGPathAddLineToPoint(path, NULL, currentP.x, currentP.y); 
      currentP = [self pointForMapPoint:parser.dataPaths[i]->points[2]]; 
      CGPathAddLineToPoint(path, NULL, currentP.x, currentP.y); 
      currentP = [self pointForMapPoint:parser.dataPaths[i]->points[3]]; 
      CGPathAddLineToPoint(path, NULL, currentP.x, currentP.y); 
      currentP = [self pointForMapPoint:parser.dataPaths[i]->points[0]]; 
      CGPathAddLineToPoint(path, NULL, currentP.x, currentP.y); 
      CGPathCloseSubpath(path); 
      CGContextSetFillColorWithColor(context, colors[parser.dataPaths[i]->dataVal]); 
      CGContextAddPath(context, path); 
      CGContextFillPath(context); 
      CGPathRelease(path); 
     } 
} 

Le nouveau drawMapRect: zoomScale: inContext: Code

- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context { 

    // We have to pad the map rect a lot to allow for visibility testing that works well. 
    radarImageView.testMapRect = MKMapRectMake(mapRect.origin.x - 40000, mapRect.origin.y - 40000, mapRect.size.width + 40000, mapRect.size.height + 40000); 

    radarImageView.frame = [self rectForMapRect:self.overlay.boundingMapRect]; 
    [radarImageView setNeedsDisplay]; 

} 

La méthode drawRect du UIView personnalisé.

- (void)drawRect:(CGRect)rect { 


    CGContextRef context = UIGraphicsGetCurrentContext(); 

    int numPaths = parser.dataPaths.size(); 

    CGContextSetAlpha(context, 1); 
    for (int i = 0; i < numPaths; i++) { 

     // Make sure the bin is actually visible in this region before drawing it 
     if (MKMapRectContainsPoint(testMapRect, parser.dataPaths[i]->points[0]) || 
      MKMapRectContainsPoint(testMapRect, parser.dataPaths[i]->points[1]) || 
      MKMapRectContainsPoint(testMapRect, parser.dataPaths[i]->points[2]) || 
      MKMapRectContainsPoint(testMapRect, parser.dataPaths[i]->points[3])) { 

      CGMutablePathRef path = CGPathCreateMutable(); 
      CGPoint currentP = [(RadarImageOverlayView *)self.superview pointForMapPoint:parser.dataPaths[i]->points[0]]; 

      CGContextBeginPath(context); 
      CGPathMoveToPoint(path, NULL, currentP.x, currentP.y); 

      currentP = [(RadarImageOverlayView *)self.superview pointForMapPoint:parser.dataPaths[i]->points[1]]; 
      CGPathAddLineToPoint(path, NULL, currentP.x, currentP.y); 

      currentP = [(RadarImageOverlayView *)self.superview pointForMapPoint:parser.dataPaths[i]->points[2]]; 
      CGPathAddLineToPoint(path, NULL, currentP.x, currentP.y); 

      currentP = [(RadarImageOverlayView *)self.superview pointForMapPoint:parser.dataPaths[i]->points[3]]; 
      CGPathAddLineToPoint(path, NULL, currentP.x, currentP.y); 

      currentP = [(RadarImageOverlayView *)self.superview pointForMapPoint:parser.dataPaths[i]->points[0]]; 
      CGPathAddLineToPoint(path, NULL, currentP.x, currentP.y); 

      CGPathCloseSubpath(path); 
      CGContextSetFillColorWithColor(context, colors[parser.dataPaths[i]->dataVal]); 
      CGContextAddPath(context, path); 
      CGContextFillPath(context); 
      CGPathRelease(path); 
     } 
    } 
} 

Merci!

EDIT

Je pense que le problème a quelque chose à voir avec le contexte de la RadarImageView. Y a-t-il un problème avec la façon dont j'obtiens le contexte dans la méthode drawRect: peut-être?

Répondre

2

Vous ne pouviez pas préparer votre chemin (s) avant l'appel de drawMapRect. Par exemple lorsque la région visible change. Vous devez simplement ajouter le chemin au contexte de dessin dans drawMapRect. Je pense que vous pourriez peut-être même préparer les chemins pour une échelle donnée, puis faire un panoramique et mettre à l'échelle le contexte (CGContextScaleCTM) lorsque la région change.

Si les données ne changent pas souvent. Une autre optimisation serait de préparer des images au format png pour le niveau de zoom inférieur dès que vous obtenez les données. Pour le niveau de zoom supérieur, vous pouvez continuer à dessiner comme vous le faites.

Pour réduire le nombre d'itérations, vous pouvez utiliser le pavage avec vos données: au lieu d'avoir un seul grand tableau avec toutes vos données, vous pouvez avoir un tableau pour chaque mosaïque. Dans un premier temps, vous récupérez les tableaux correspondant à la mosaïque intersectant la région visible courante, puis vous bouclez uniquement sur ces tableaux. Bien sûr, cela ne fonctionnerait que pour un niveau de zoom plus élevé.

Si vous ne souhaitez pas optimiser, vous pouvez améliorer l'expérience utilisateur dans les cas où de nombreux chemins sont affichés. Pour permettre à l'utilisateur d'interagir avec la carte lors de la construction des chemins, vous ne devez pas traiter tous les éléments dans une boucle. Vous pouvez traiter 1000 chemins à la fois, puis utiliser performSelector: afterDelay: pour retarder le traitement du lot suivant. De cette façon, vous pouvez afficher une barre de progression et laisser l'utilisateur interagir avec la carte.

+0

Ahh c'est une bonne idée. Je vais essayer et vous faire savoir comment cela fonctionne. –

+0

J'ai essayé ceci et la performance n'était toujours pas assez bonne. Le nombre typique de chemins est d'environ 30 000. Il y a un chemin pour chaque point de données dans le domaine du radar (les zéros ne sont pas comptés). Ce nombre pourrait théoriquement atteindre 165 000. –

+0

Quel est le nombre total de chemins? Quel est le nombre de chemins visibles? L'utilisateur peut-il changer le zoom? Pouvez-vous avoir tous les chemins visibles? – FKDev

3

Je suggère de jeter un oeil à l'échantillon HazardMap par Apple. Il a quelques bons exemples de faire exactement ce que vous recherchez. Le KMLViewer peut également aider!