5

I am using mapkit on iPhone with iOS 4. I am using a custom overlay and a custom overlay view, to draw shapes on the map. At the moment, shapes are just rectangles, but I am planning something more sophisticated. This is why I am not using the MKPolygon overlay type. This is the code for my overlay view drawing method:

-(void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context
{
    // Clip context to bounding rectangle
    MKMapRect boundingMapRect = [[self overlay] boundingMapRect];
    CGRect boundingRect = [self rectForMapRect:boundingMapRect];
    CGContextAddRect(context, boundingRect);
    CGContextClip(context);

    // Define shape
    CGRect shapeRect = CGRectMake(0.5f, 0.5f, boundingRect.size.width - 1.0f, boundingRect.size.height - 1.0f);

    // Fill
    CGContextSetRGBFillColor(context, 0.5f, 0.5f, 0.5f, 0.5f);
    CGContextFillRect(context, shapeRect);

    // Stroke
    CGContextSetRGBStrokeColor(context, 0, 0, 0, 0.75f);
    CGContextSetLineWidth(context, 1.0f);
    CGContextStrokeRect(context, shapeRect);
}

The problem is that rectangles get correctly filled (so it appears their bounding rect is correctly set), but they don't get stroked. Can anyone help? Thanks!

Giorgio Barchiesi
  • 6,109
  • 3
  • 32
  • 36
  • While I don't know the solution, I just wanted to draw your attention to [CGRectInset](http://developer.apple.com/library/ios/DOCUMENTATION/GraphicsImaging/Reference/CGGeometry/Reference/reference.html#//apple_ref/c/func/CGRectInset) (instead of creating the rectangle with `CGRectMake`). – DarkDust Nov 25 '11 at 08:25
  • Just occurred to me: does the rectangle get stroked if you further inset it? So first you fill it, then you inset the rect by another 0.5 and then stroke it? – DarkDust Nov 25 '11 at 08:30
  • 1
    Found another question related to this: http://stackoverflow.com/questions/5274164/custom-mkoverlayview-line-width so it seems the problem is with line width scaling, and it looks like I have to fix my code this way: CGContextSetLineWidth(context, 5.0f / zoomScale); – Giorgio Barchiesi Nov 25 '11 at 09:11
  • I also discovered that the bounding rect origin is not 0, 0, so another fix to my code is: CGRect shapeRect = CGRectMake(boundingRect.origin.x + 0.5f, boundingRect.origin.y + 0.5f, boundingRect.size.width - 1.0f, boundingRect.size.height - 1.0f); – Giorgio Barchiesi Nov 25 '11 at 09:16
  • Then you should write an answer describing the solution and accept it. – DarkDust Nov 25 '11 at 09:30
  • @DarkDust is right about CGRectInset, so it is: CGRect shapeRect = CGRectInset(boundingRect, 2.5f / zoomScale, 2.5f / zoomScale); this way the inset is also correctly scaled! Let me test it a bit more, and I'll write an answer. Thank you. – Giorgio Barchiesi Nov 25 '11 at 09:33

2 Answers2

2

As reported in some previous comments, the problem is with line width. More generally, all drawing is automatically scaled to follow the map zooming, so if you want some of your drawing metrics to be zoom-independent, you have to divide it by zoomScale.

Here is the new code, that works correctly on my iPhone 4:

-(void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context
{
    // Clip context to bounding rectangle
    MKMapRect boundingMapRect = [[self overlay] boundingMapRect];
    CGRect boundingRect = [self rectForMapRect:boundingMapRect];
    CGContextAddRect(context, boundingRect);
    CGContextClip(context);

    // Define shape
    CGRect shapeRect = CGRectInset(boundingRect, 2.0f / zoomScale, 2.0f / zoomScale);

    // Fill
    CGContextSetRGBFillColor(context, 0.5f, 0.5f, 0.5f, 0.5f);
    CGContextFillRect(context, shapeRect);

    // Stroke
    CGContextSetRGBStrokeColor(context, 0, 0, 0, 0.75f);
    CGContextSetLineWidth(context, 4.0f / zoomScale);
    CGContextStrokeRect(context, shapeRect);
}

I will also report the code I am using in the overlay to calculate and return the bounding rectangle, because I think it can help:

-(MKMapRect)boundingMapRect
{
    // Overlay bounds
    CLLocationCoordinate2D topLeftcoordinate = <the top-left coordinate of overlay>;
    CLLocationCoordinate2D bottomRightCoordinate = <the bottom-right coordinate of overlay>;

    // Convert them to map points
    MKMapPoint topLeftPoint = MKMapPointForCoordinate(topLeftcoordinate);
    MKMapPoint bottomRightPoint = MKMapPointForCoordinate(bottomRightCoordinate);

    // Calculate map rect
    return MKMapRectMake(topLeftPoint.x, topLeftPoint.y, bottomRightPoint.x - topLeftPoint.x, topLeftPoint.y - bottomRightPoint.y);
}

Thank you all for your comments and suggestions.

Giorgio Barchiesi
  • 6,109
  • 3
  • 32
  • 36
0
  1. Currently, we should use MKOverlayRenderer instead of MKOverlayView.
  2. In method -(void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context, use scaleFactor for configuring the stroke width of lines or other attributes that might be affected by the scale of the map’s contents.

Refer to Apple official site drawMapRect:zoomScale:inContext: of MKOverlayRenderer.

HongchaoZhang
  • 3,494
  • 1
  • 17
  • 8