11
votes

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? Cheerz

4
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
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
thanks anyway, +1 it saved me some timeigrek
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

4 Answers

22
votes

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



    }

}
5
votes

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
2
votes
//show member pins
        for (id member in members) {
            [resultMapView addAnnotation:(Member *) member];
        }

This can be replaced with

[resultMapView addAnnotations:members];
0
votes

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

    //...
}