0

It seems like many have touched on this issue of overlapping MKAnnotation Views, and gracefully spreading them out at the end of a zoom, and at viewdidload. So I think I am just going to bite the bullet and dev out a solution, but before I do I wanted to ask advice on the algo to implement. Previous posts about this topic that had no viable solution offered:

ios sdk MKMapView overlapping pins? Laying out overlapping rectangles

and many more...

So the function I am thinking of is this: (Note all the CGRects are the same size)

-(NSArray *)spreadOutTheseViews:(NSArray *)arrayofCGRects{



    ////////////////////////////////////
    //find out who overlaps with who..
    ////////////////////////////////////

    NSMutableArray *arrayOfArraysOfRects = [[NSMutableArray alloc] init];

    // 1. make an array to let you know which Rects have already been added to one of the sets
    int n = (int)[arrayofCGRects count];
    NSMutableArray *addedAlready = [[NSMutableArray alloc] init];
    for (id obj in arrayofCGRects ) {
        [addedAlready addObject:@0];
    }

    // 2. go through and add them if they overlap
    for (int i=0; i<[arrayofCGRects count]; i++) {
        if ([[addedAlready objectAtIndex:i] isEqualToValue:@(0)]) {

            CGRect rect1 = [[arrayOfArraysOfRects objectAtIndex:i] CGRectValue];
            [arrayOfArraysOfRects addObject:[[NSMutableArray alloc] initWithObjects:@(i), nil]];
            [addedAlready insertObject:@(1) atIndex:i];

            for (int j=0; j<[arrayofCGRects count]; j++){
                if (i!=j && [[addedAlready objectAtIndex:j] isEqualToValue:@(0)] ) {
                    CGRect rect2 = [[arrayOfArraysOfRects objectAtIndex:j] CGRectValue];
                    if (CGRectIntersectsRect(rect1,rect2)){
                        // check if they are overlapping, if so, add it to
                        [[arrayOfArraysOfRects lastObject] addObject:@(j)];
                        [addedAlready insertObject:@(1) atIndex:j];
                    }
                }
            }
        }
    }
    NSMutableArray *arrayofCGPoints = [[NSMutableArray alloc] init];
    //3. now that we have "clusters" of overlapping rects.. push them away from each other..
    for(NSArray *indexArray in arrayOfArraysOfRects){

        if ([indexArray count]==1){
            // just simply add the CGPoint
            CGRect rect =  [[arrayofCGRects objectAtIndex:(NSInteger)[indexArray firstObject]] CGRectValue];
            [arrayofCGPoints addObject:[NSValue valueWithCGPoint:rect.origin] ];

        }else{

            // heart of the algo..
            // Idea 1: get the average of the CGPoints to find the center of all of them, then find a ring with a specific radius that we can put them all on. Note this is a packing problem that I really dont have a better solution for..


        }



    }

    return arrayofCGPoints;

}

Any help here would be greatly appreciated..

Community
  • 1
  • 1
fchaubard
  • 91
  • 4

1 Answers1

0

Well its not the prettiest of solutions.. but I got something working for now in case someone finds this useful.. Also please still post if you have a more elegant solution!

-(NSArray *)spreadOutTheseViews:(NSArray *)arrayofCGRects{

bool doAnyOverlapStill = true;
int counter=0;
NSMutableArray *newArrayOfCGRects = [[NSMutableArray alloc] initWithArray:arrayofCGRects];

while (doAnyOverlapStill) {
    NSLog([NSString stringWithFormat:@"number of iters %d",counter]);

    for (int i=0; i<[arrayofCGRects count]; i++) {
        CGRect rect1 = [[newArrayOfCGRects objectAtIndex:i] CGRectValue];
        for (int j=0; j<=i; j++){
            if (i!=j) {
                CGRect rect2 = [[newArrayOfCGRects objectAtIndex:j] CGRectValue];
                bool intersect = CGRectIntersectsRect(rect1,rect2);
                if (intersect){

                    if (counter%4==0) {
                        // LEFT
                        NSLog(@"LEFT");
                        // which ever one is on the left, move it further to the left
                        double neededDistanceAdjustment =  5+MAX(rect1.size.width,rect2.size.width) - fabsf(rect2.origin.x - rect1.origin.x);
                        if (rect1.origin.x < rect2.origin.x) {
                            // move rect 1 left (subtract)
                            rect1.origin.x -= neededDistanceAdjustment;
                            [newArrayOfCGRects removeObjectAtIndex:i];
                            [newArrayOfCGRects insertObject:[NSValue valueWithCGRect:rect1] atIndex:i];

                        }else{
                            // move rect 2 left (subtract)
                            rect2.origin.x -= neededDistanceAdjustment;
                            [newArrayOfCGRects removeObjectAtIndex:j];
                            [newArrayOfCGRects insertObject:[NSValue valueWithCGRect:rect2] atIndex:j];
                        }

                    }else if (counter%4==1) {
                        // UP
                        NSLog(@"UP");
                        double neededDistanceAdjustment =  5+MAX(rect1.size.height,rect2.size.height) - fabsf(rect2.origin.y - rect1.origin.y);
                        if (rect1.origin.y < rect2.origin.y) {
                            // move rect 1 up (subtract)
                            rect1.origin.y -= neededDistanceAdjustment;
                            [newArrayOfCGRects removeObjectAtIndex:i];
                            [newArrayOfCGRects insertObject:[NSValue valueWithCGRect:rect1] atIndex:i];

                        }else{
                            // move rect 2 up (subtract)
                            rect2.origin.y -= neededDistanceAdjustment;
                            [newArrayOfCGRects removeObjectAtIndex:j];
                            [newArrayOfCGRects insertObject:[NSValue valueWithCGRect:rect2] atIndex:j];
                        }

                    }else if (counter%4==2) {
                        //RIGHT

                        NSLog(@"RIGHT");
                        double neededDistanceAdjustment =  5+MAX(rect1.size.width,rect2.size.width) - fabsf(rect2.origin.x - rect1.origin.x);

                        if (rect1.origin.x > rect2.origin.x) {
                            // move rect 1 right (add)
                            rect1.origin.y += neededDistanceAdjustment;
                            [newArrayOfCGRects removeObjectAtIndex:i];
                            [newArrayOfCGRects insertObject:[NSValue valueWithCGRect:rect1] atIndex:i];

                        }else{
                            // move rect 2 right (add)
                            rect2.origin.y += neededDistanceAdjustment;
                            [newArrayOfCGRects removeObjectAtIndex:j];
                            [newArrayOfCGRects insertObject:[NSValue valueWithCGRect:rect2] atIndex:j];
                        }

                    }else if (counter%4==3) {
                        // DOWN

                        NSLog(@"DOWN");
                        double neededDistanceAdjustment =  5+MAX(rect1.size.height,rect2.size.height) - fabsf(rect2.origin.y - rect1.origin.y);
                        if (rect1.origin.y > rect2.origin.y) {
                            // move rect 1 down (add)
                            rect1.origin.y += neededDistanceAdjustment;
                            [newArrayOfCGRects removeObjectAtIndex:i];
                            [newArrayOfCGRects insertObject:[NSValue valueWithCGRect:rect1] atIndex:i];

                        }else{
                            // move rect 2 down (add)
                            rect2.origin.y += neededDistanceAdjustment;
                            [newArrayOfCGRects removeObjectAtIndex:j];
                            [newArrayOfCGRects insertObject:[NSValue valueWithCGRect:rect2] atIndex:j];
                        }
                    }
                    // update newArrayOfCGRects
                    counter++;

                }
            }
        }
    }
    if (counter>100) {
        NSLog(@"error!!");
        return arrayofCGRects;


    }

    doAnyOverlapStill = [self doRectsOverlap:newArrayOfCGRects];
    if (!doAnyOverlapStill) {
        return newArrayOfCGRects;
    }
}

return arrayofCGRects;

}
fchaubard
  • 91
  • 4
  • Clarification question: are you talking about point clustering, or about the annotations views not overlapping each other for aesthetic reasons? – incanus Nov 10 '14 at 18:42
  • Yes, the latter, as better demonstrated here: http://stackoverflow.com/questions/17710770/ios-sdk-mkmapview-overlapping-pins – fchaubard Nov 10 '14 at 23:58