2011-07-18 36 views
5

カスタムMKOverlayを使用して、気象データをMKMapViewに描画しています。この図はCoreGraphicsで行われています。この特定の場合、drawMapRect:zoomScale:inContext:メソッドで描画を行うだけでは十分ではありません。 drawMapRectメソッドのようにタイル張りではなく、一度に描画する必要があります。カスタムMKOverlayの描画

以前は.gifでレーダー画像を持っていたので、imageViewを追加してdrawMapRectのimageViewフレームのサイズを変更しました。

私の計画はこれに似た何かをすることでした。カスタムUIViewを追加し、drawMapRectでsetNeedsDisplayを呼び出します。

ここに関連コードがあります。

MKOverlayオブジェクトのboundingMapRectプロパティ:

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

作業drawMapRect:zoomScale:inContext:コード(すなわち、遅すぎます)。

- (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); 
     } 
} 

新しいdrawMapRect:zoomScale:inContext:コード

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

} 

カスタムUIViewのののdrawRectメソッド。

- (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); 
     } 
    } 
} 

ありがとうございます!

EDIT

私はこの問題はRadarImageViewの文脈とは何かを持っていることを考えています。 drawRect:メソッドのコンテキストを取得する方法に問題がありますか?

答えて

2

drawMapRectが呼び出される前にパスを準備できませんでしたか?例えば、可視領域が変化するとき。 drawMapRectの描画コンテキストへのパスを追加するだけで済みます。指定されたスケールのパスを準備し、領域が変化したときにコンテキスト(CGContextScaleCTM)をパンしてスケールすることもできます。

データが頻繁に変更されない場合。もう1つの最適化は、データを取得するとすぐに、低いズームレベルの画像をpng形式で準備することです。より高いズームレベルのために、あなたのように引き続き描画することができます。

データのタイリングを減らすには、すべてのデータが1つの大きな配列ではなく、タイルごとに1つの配列を持つことができます。最初のステップでは、現在の可視領域と交差するタイルに対応する配列を取得し、次にこれらの配列だけをループします。もちろん、それは高いズームレベルでのみ動作します。

最適化したくない場合は、多くのパスが表示されている場合のユーザーエクスペリエンスを向上させることができます。ユーザーがパスを構築しているときにマップを操作できるようにするには、すべての要素を1つのループで処理しないでください。一度に1000のパスを処理し、performSelector:afterDelay:を使用して、次のバッチの処理を遅らせることができます。こうすることで、進行状況バーを表示して、ユーザーがマップと対話できるようになります。

+0

これは良いアイデアです。私はそれに打撃を与え、それがどのように機能するかを伝えます。 –

+0

これを試しても、パフォーマンスはまだ十分ではありませんでした。パスの典型的な数は約30,000です。レーダーのドメイン内の各データポイントのパスがあります(ゼロは数えられません)。理論的にはこの数は165000と大きくなる可能性があります。 –

+0

パスの総数はいくらですか?表示されるパスの数は何ですか?ユーザーはズームを変更できますか?すべてのパスを表示できますか? – FKDev

3

AppleがHazardMapサンプルを見てみることをお勧めします。それはあなたが何をしているかを正確に行う良い例がいくつかあります。

KMLViewerもお手伝いできます。

関連する問題