0
votes

Can anyone answer any of these two questions with regards to the below

1) If I size my overlay objects dynamically according to their zoomScale within MKOverlayPathView how can I ensure that the MKOverlay protocol supporting class returns the correct boundingMapRect when it doesn't know what the zoomScale will be?

2) What are the side effects of a boundingMapRect that is too small?

Original ....

I an MKMapView with one MKOverlayPathView. With the overlay added to the map panning and zooming leaves sections of the underlying map blurry or, in some cases, un-downloaded. The overlay view itself is fine and in focus, it's the map data that is left fuzzy (from the previous zoom level).

If the overlays are not added everything works fine.

The MKOverlayPathView is subclassed and has an array of points and sizes, it uses these points and sizes to draw filled circles into the singular overlay for display. The bounding rect is formed from a union of all points accounting for the radius of the circles they will produce.

So what am I missing? Is it the drawing process, the boundingRect or something else?

Sample code:

MapViewController Code

- (MKOverlayView *)mapView:(MKMapView *)map viewForOverlay:(id <MKOverlay>)overlay
{
    if ([overlay isKindOfClass:[MultiCluster class]]) {
        MKMultiClusterView *multiclusterView = [[MKMultiClusterView alloc] initWithOverlay:overlay];
        return multiclusterView;
    }

    return nil;
}

MultiCluster.h

@interface MultiCluster : NSObject <MKOverlay> {
    NSArray *_clusters;
    MKMapRect _boundingMapRect;
}

- (id)initWithClusters:(NSArray *)clusters;

@property (nonatomic, strong) NSArray *clusters;

@end

MultiCluster.m

@implementation MultiCluster

@synthesize clusters = _clusters;

- (id)initWithClusters:(NSArray *)clusters {
    if (self = [super init]) {
        _clusters = [clusters copy];

        NSUInteger clusterCount = [_clusters count];
        if (clusterCount) {
            _boundingMapRect = [[_clusters objectAtIndex:0] boundingMapRect];
            NSUInteger i;
            for (i = 1; i < clusterCount; i++) {
                _boundingMapRect = MKMapRectUnion(_boundingMapRect, [[_clusters objectAtIndex:i] boundingMapRect]);
            }
        }
    }

    return self;
}

- (void)setClusters:(NSArray *)clusters {
    _clusters = [clusters copy];
    NSUInteger clusterCount = [_clusters count];
    if (clusterCount) {
        _boundingMapRect = [[_clusters objectAtIndex:0] boundingMapRect];
        NSUInteger i;
        for (i = 1; i < clusterCount; i++) {
            _boundingMapRect = MKMapRectUnion(_boundingMapRect, [[_clusters objectAtIndex:i] boundingMapRect]);
        }
    }
}

- (MKMapRect)boundingMapRect {
    return _boundingMapRect;
}

- (CLLocationCoordinate2D)coordinate {
    return MKCoordinateForMapPoint(MKMapPointMake(MKMapRectGetMidX(_boundingMapRect), MKMapRectGetMidY(_boundingMapRect)));
}

@end

MKMultiClusterView.h

@interface MKMultiClusterView : MKOverlayPathView {
    CGColorRef *colors;
}

@end

MKMultiClusterView.m

@implementation MKMultiClusterView

// Create a table of possible colors to draw a grid cell with
- (void)initColors
{
    CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
    colors = malloc(sizeof(CGColorRef) * NUM_COLORS);
    int i = 0;
    colors[i++] = CGColorCreate(rgb, (CGFloat[]){ .588, .294, .78, OA }); // 1.00
    colors[i++] = CGColorCreate(rgb, (CGFloat[]){ .784, .471, .82, OA }); // 0.77
    colors[i++] = CGColorCreate(rgb, (CGFloat[]){ 1, 0, 0, OA }); // 0.59
    colors[i++] = CGColorCreate(rgb, (CGFloat[]){ 1, .392, 0, OA }); // 0.46
    colors[i++] = CGColorCreate(rgb, (CGFloat[]){ 1, .392, 0, OA }); // 0.35
    colors[i++] = CGColorCreate(rgb, (CGFloat[]){ 1, .784, 0, OA }); // 0.27
    colors[i++] = CGColorCreate(rgb, (CGFloat[]){ 1, 1, .5, OA }); // 0.21
    colors[i++] = CGColorCreate(rgb, (CGFloat[]){ .745, .941, .467, OA }); // 0.16
    colors[i++] = CGColorCreate(rgb, (CGFloat[]){ .122, 1, .31, OA }); // 0.12
    colors[i++] = CGColorCreate(rgb, (CGFloat[]){ .588, 1, .941, OA }); // 0.10
    colors[i++] = CGColorCreate(rgb, (CGFloat[]){ .784, 1, 1, OA }); // 0.08
    colors[i++] = CGColorCreate(rgb, (CGFloat[]){ .843, 1, 1, OA }); // 0.06
    colors[i++] = CGColorCreate(rgb, (CGFloat[]){ .902, 1, 1, OA }); // 0.04
    colors[i] = CGColorCreate(rgb, (CGFloat[]){ .784, .784, .784, OA }); // 0.03
    CGColorSpaceRelease(rgb);
}

// Look up a color in the table of colors for a peak ground acceleration
- (CGColorRef)colorForSize:(int)value
{
    if (value > 3000) return colors[0];
    if (value > 2500) return colors[1];
    if (value > 2000) return colors[2];
    if (value > 1500) return colors[3];
    if (value > 1000) return colors[4];
    if (value > 540) return colors[5];
    if (value > 480) return colors[6];
    if (value > 420) return colors[7];
    if (value > 360) return colors[8];
    if (value > 300) return colors[9];
    if (value > 240) return colors[10];
    if (value > 180) return colors[11];
    if (value > 120) return colors[12];
    if (value <= 120) return colors[13];
    return NULL;
}

- (id)initWithOverlay:(id<MKOverlay>)overlay {
    if (self = [super initWithOverlay:overlay])
    {
        [self initColors];
    }
    return self;
}

- (CGPathRef)createPath:(Cluster *)cluster zoomScale:(MKZoomScale)zoomScale {
    CGMutablePathRef path = CGPathCreateMutable();
    // Calculate radius for circle in map pixels.    
    double fudge = 2;
    double viewWidth = 320;
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { 
        fudge = 5;
        viewWidth = 768;
    }
    // Radius of circle in pixels.
    int radiusPixels = 10 + (fudge * log([cluster.members count]));
    fudge = 1/zoomScale;
    double radiusPoints = radiusPixels * fudge;
    double diameterPoints = radiusPoints * 2;

    // Center of circle in map points.
    CGPoint relativePoint = [self pointForMapPoint:MKMapPointForCoordinate(cluster.coordinate)];
    CGRect ellipseRect = CGRectMake(relativePoint.x - radiusPoints, 
                                    relativePoint.y - radiusPoints, 
                                    10000, 
                                    10000);
    CGPathAddEllipseInRect(path, NULL, ellipseRect);

    return path;
}

- (void)drawMapRect:(MKMapRect)mapRect
          zoomScale:(MKZoomScale)zoomScale
          inContext:(CGContextRef)context
{
    MultiCluster *multiCluster = (MultiCluster *)self.overlay;
    for (Cluster *cluster in multiCluster.clusters) {
        CGPathRef path = [self createPath:cluster zoomScale:zoomScale];
        if (path) {
            self.fillColor = [UIColor colorWithCGColor:[self colorForSize:[cluster.members count]]];
            [self applyFillPropertiesToContext:context atZoomScale:zoomScale];
            CGContextBeginPath(context);
            CGContextAddPath(context, path);
            CGContextDrawPath(context, kCGPathEOFill);
            CGPathRelease(path);
        }
    }
}

@end

MKMultiClusterView has some test code in it, such as unused variables and fixed rather than dynamic circle size. It's unrelated to the problem (doesn't matter if circles are fixed or variable in size, problem still presents itself).

Comment if you want more code to be included and i'll find somewhere to upload it as a sample project or include the relevant sections in this question.

A completely static implementation of the same code does not present a problem, correction it does with alpha introduced. So can I safely say it's the boundingMapRect that is the problem?

1

1 Answers

1
votes

Have you tried moving the alpha component from the individual circles to the layer itself?