11

I'm trying to set the map region (center and span) so that the map shows all pin-annotations at the same time.

I'm having trouble converting the long/lat coordinates from NSString to double, resp. make calculations with them. Here is the code I'm using:

- (void)updateMemberPins{

//calculate new region to show on map
double center_long = 0.0f;
double center_lat = 0.0f;
double max_long = 0.0f;
double min_long = 0.0f;
double max_lat = 0.0f;
double min_lat = 0.0f;

for (Member *member in members) {
    
    //find min and max values
    if ([member.locLat doubleValue] > max_lat) {max_lat = [member.locLat doubleValue];}
    if ([member.locLat doubleValue] < min_lat) {min_lat = [member.locLat doubleValue];}
    if ([member.locLong doubleValue] > max_long) {max_long = [member.locLong doubleValue];}
    if ([member.locLong doubleValue] < min_long) {min_long = [member.locLong doubleValue];}
    
    //sum up long and lang to get average later
    center_lat = center_lat + [member.locLat doubleValue];
    center_long = center_long + [member.locLong doubleValue];
}

//calculate average long / lat
center_lat = center_lat / [members count];
center_long = center_long / [members count];

NSLog(@"center long: %d, center lat: %d", center_long, center_lat);
NSLog(@"max_long: %d, min_long: %d, max_lat: %d, min_lat: %d", max_long, min_long, max_lat, min_lat);
    
//create new region and set map
CLLocationCoordinate2D coord = {latitude: center_lat, longitude: center_long};
MKCoordinateSpan span = MKCoordinateSpanMake(abs(max_lat) + abs(min_lat), abs(max_long) + abs(min_long));
MKCoordinateRegion region = {coord, span};
[resultMapView setRegion:region];

//remove all pins from map
[resultMapView removeAnnotations:resultMapView.annotations];

//show member pins
for (id member in members) {
    [resultMapView addAnnotation:(Member *) member];
}

}

The result of the log-output is:

center long: -1946827116, center lat: 1075651472

max_long: -6267216, min_long: 1076018553, max_lat: 0, min_lat: 0

I think the problem comes from (wrongly) converting values from NSString to double, however I cannot find a way to make it work... The format of the location-strings is like '43.5686473'.

Any hints?

starball
  • 20,030
  • 7
  • 43
  • 238
pawi
  • 335
  • 1
  • 2
  • 8
  • your start values for min max lat lon are wrong. Try these: double max_long = -360.0f; double min_long = 360.0f; double max_lat = -360.0f; double min_lat = 360.0f; – igrek May 14 '13 at 12:58
  • and span calculations? why add them? And also abs returns int not double so should be fabs: MKCoordinateSpan span = MKCoordinateSpanMake(fabs(max_lat - min_lat), fabs(max_long - min_long)); – igrek May 14 '13 at 13:05
  • thanks anyway, +1 it saved me some time – igrek May 14 '13 at 13:06
  • and also no need to accumulate lats lons in a loop. These can be calculated from min max values: center_lat = (min_lat + max_lat) / 2.0; center_long = (min_long + max_long) / 2.0; – igrek May 14 '13 at 13:09

4 Answers4

22

The final, working code for those looking for something similiar:

    - (void)updateMemberPins{

    //remove all pins from map
    [resultMapView removeAnnotations:resultMapView.annotations];

    if ([members count] > 0) {

        @try {
            //calculate new region to show on map           
            Member *firstMember = [members objectAtIndex:0];
            double max_long = [firstMember.locLong doubleValue];
            double min_long = [firstMember.locLong doubleValue];
            double max_lat = [firstMember.locLat doubleValue];
            double min_lat = [firstMember.locLat doubleValue];

            //find min and max values
            for (Member *member in members) {
                if ([member.locLat doubleValue] > max_lat) {max_lat = [member.locLat doubleValue];}
                if ([member.locLat doubleValue] < min_lat) {min_lat = [member.locLat doubleValue];}
                if ([member.locLong doubleValue] > max_long) {max_long = [member.locLong doubleValue];}
                if ([member.locLong doubleValue] < min_long) {min_long = [member.locLong doubleValue];}
            }

            //calculate center of map
            double center_long = (max_long + min_long) / 2;
            double center_lat = (max_lat + min_lat) / 2;

            //calculate deltas
            double deltaLat = abs(max_lat - min_lat);
            double deltaLong = abs(max_long - min_long);

            //set minimal delta
            if (deltaLat < 5) {deltaLat = 5;}
            if (deltaLong < 5) {deltaLong = 5;}

            //debug
            //NSLog(@"center long: %f, center lat: %f", center_long, center_lat);
            //NSLog(@"max_long: %f, min_long: %f, max_lat: %f, min_lat: %f", max_long, min_long, max_lat, min_lat);

            //create new region and set map
            CLLocationCoordinate2D coord = {latitude: center_lat, longitude: center_long};
            MKCoordinateSpan span = MKCoordinateSpanMake(deltaLat, deltaLong);
            MKCoordinateRegion region = {coord, span};
            [resultMapView setRegion:region];


        }
        @catch (NSException * e) {
            NSLog(@"Error calculating new map region: %@", e);
        }
        @finally {
            //show member pins
            for (id member in members) {
                [resultMapView addAnnotation:(Member *) member];
            }
        }



    }

}
Ankit Srivastava
  • 12,347
  • 11
  • 63
  • 115
pawi
  • 335
  • 1
  • 2
  • 8
5

To show double value in NSLog(), you should use %f, instead of %d Change NSLog() part like this:

NSLog(@"center long: %f, center lat: %f", center_long, center_lat);
NSLog(@"max_long: %f, min_long: %f, max_lat: %f, min_lat: %f", max_long, min_long, max_lat, min_lat);

Also, using region from MKMapView is much simpler than making your own. Once it's set with zoom ratio, all you need is to dynamically move around the map with different coordinates.

MKCoordinateRegion region = self.mapView.region;
region.center = centerCoordinate;
region.span.longitudeDelta /= ratioZoomMax; // Bigger the value, closer the map view
region.span.latitudeDelta /= ratioZoomMax;
[self.mapView setRegion:region animated:YES]; // Choose if you want animate or not
petershine
  • 3,190
  • 1
  • 25
  • 49
  • Hey Peter! Thanks for your response. You are right with the NSLog statement. However, this guess this is not causing my miscalculations... The ratioZoom variant is nice, but I don't know this ratio. I have 'the outest coordiantes' that have to be visible on the map... – pawi Feb 18 '11 at 13:42
  • Well, it turned out that I was just confused with the bad numbers that the wrong Log-statement gave me. Then I had mad some mistakes in my calcs - and also bad data (0, 0 coordinates). – pawi Feb 18 '11 at 14:55
  • You can change `ratioZoomMax` to be smaller to show much wider area, showing pins that are far from one another. Reading your code again, I found that you had made unnecessary addition to find center value. As you've already figured out, to find the center, you just have to do `(lowestValue + highestValue)/2.0`, not dividing it by `[members count]` – petershine Feb 19 '11 at 02:27
  • Correct! This makes it even simpler ;-) I'll post my final code that is now working as it should. Thank you Peter! – pawi Feb 21 '11 at 08:07
  • You are welcome~ I simply shared what I learned. Glad it was helpful – petershine Feb 21 '11 at 15:15
2
//show member pins
        for (id member in members) {
            [resultMapView addAnnotation:(Member *) member];
        }

This can be replaced with

[resultMapView addAnnotations:members];
zurbergram
  • 421
  • 6
  • 20
0

You could simply use this peace of code:

-(void)updateMemberPins
{
    if([members count] == 0)
        return;

    double minLat = 90;
    double minLon = 180;
    double maxLat = -90;
    double maxLon = -180;

    for(Member *member in members)
    {
        minLat = fmin(minLat, [member.locLat doubleValue]);
        minLon = fmin(minLon, [member.locLong doubleValue]);
        maxLat = fmax(maxLat, [member.locLat doubleValue]);
        maxLon = fmax(maxLon, [member.locLong doubleValue]);
    }

    NSLog(@"MAX LAT: %f, MIN LAT: %f, MAX LONG: %f, MIN LONG: %f,", maxLat, minLat, maxLon, minLon);

    double midLat =  (minLat + maxLat)/2;
    double midLong = (minLon + maxLon)/2;

    double deltaLat = abs(maxLat - minLat);
    double deltaLong = abs(maxLon - minLon);

    if (deltaLat < 5) {deltaLat = 5;}
    if (deltaLong < 5) {deltaLong = 5;}

    //...
}
Lapinou
  • 1,467
  • 2
  • 20
  • 39