iOS MKMapView and MKPolyLine - iphone

I'm new to iOS programming and am not sure what is wrong with this code:
CLLocationCoordinate2D *locations = malloc(sizeof(CLLocationCoordinate2D) * points.count/2);
int count = 0;
for (int i = 0; i < points.count; i++)
{
CLLocationCoordinate2D point = CLLocationCoordinate2DMake([[points objectAtIndex:i] doubleValue], [[points objectAtIndex:++i] doubleValue]);
// Fill the array.
locations[count] = point;
count++;
NSLog(#"%#", locations[count-1].latitude);
NSLog(#"%#", locations[count-1].longitude);
}
// Create the polyline based on the array of points.
MKPolyline *routeLine = [MKPolyline polylineWithCoordinates:locations count:points.count/2];
MKPolylineView *routeLineView = [[MKPolylineView alloc] initWithPolyline:routeLine];
routeLineView.fillColor = [UIColor blueColor];
routeLineView.strokeColor = [UIColor blueColor];
routeLineView.lineWidth = 5;
// Add overlay to map.
[mapOutlet addOverlay:routeLine];
[mapOutlet setVisibleMapRect:routeLine.boundingMapRect];
// clear the memory allocated earlier for the points.
free(locations);
I get an EXC_BAD_ACCESS error on the first call to NSLOG(). Any thoughts?
FYI: 'points' is an array of strings containing latitude and longitude values.

When you use %# to print a value, NSLog tries to use the argument as an object pointer but these are double float values. Use the %f to print doubles:
NSLog(#"%f", locations[count-1].latitude);
NSLog(#"%f", locations[count-1].longitude);

Related

How to create a CLLocationCoordinate2d object

I am trying to to call MKPolylines' + polylineWithCoordinates:count: method.
How can I create a CLLocationCoordinate2D * object in this case. I have gone through this particular answer in CLLocationCoordinate2D without knowing how many will be in the array?
// unpacking an array of NSValues into memory
CLLocationCoordinate2D *points = malloc([mutablePoints count] * sizeof(CLLocationCoordinate2D));
for(int i = 0; i < [mutablePoints count]; i++) {
[[mutablePoints objectAtIndex:i] getValue:(points + i)];
}
MKPolyline *myPolyline = [MKPolyline polylineWithCoordinates:points count:[mutablePoints count]];
free(points);
What kind of entries are in the array mutablePoints in the above case?
If your question is simply what kind of entries are in the array, the answer is quite simple: NSValue entries. You can have a look at this guide for more info on how to use NSValues.
CLLocationCoordinate2D coordinate;
for (int i = 0; i < arr.count; i++)
{
float t1 = [[arr objectAtIndex:1] floatValue];
float t2 = [[arr objectAtIndex:0] floatValue];
coordinate.latitude = t1;
coordinate.longitude = t2;
}

iPhone: Sorting based on location

I have been working on an iPhone app, where-in i have list of users in a NSMutableArray like below.
myMutableArray: (
{
FirstName = Getsy;
LastName = marie;
Latitude = "30.237314";
Longitude = "-92.461008";
},
{
FirstName = Angel;
LastName = openza;
Latitude = "30.260329";
Longitude = "-92.450414";
},
{
FirstName = Sara;
LastName = Hetzel;
Latitude = "30.2584499";
Longitude = "-92.4135357";
}
)
I need to sort users based on the location who is nearby to my location by calculating latitude and longitude. I am not able to achieve this till now. Could someone help me on giving some samples?
UPDATED: I am trying like below as per Mr.sch suggested. Please check my updated code. Is it fine?.
NSArray *orderedUsers = [myMutableArray sortedArrayUsingComparator:^(id a,id b) {
NSArray *userA = (NSArray *)a;
NSArray *userB = (NSArray *)b;
CGFloat aLatitude = [[userA valueForKey:#"Latitude"] floatValue];
CGFloat aLongitude = [[userA valueForKey:#"Longitude"] floatValue];
CLLocation *participantALocation = [[CLLocation alloc] initWithLatitude:aLatitude longitude:aLongitude];
CGFloat bLatitude = [[userA valueForKey:#"Latitude"] floatValue];
CGFloat bLongitude = [[userA valueForKey:#"Longitude"] floatValue];
CLLocation *participantBLocation = [[CLLocation alloc] initWithLatitude:bLatitude longitude:bLongitude];
CLLocation *myLocation = [[CLLocation alloc] initWithLatitude:locationCoordinates.latitude longitude:locationCoordinates.longitude];
CLLocationDistance distanceA = [participantALocation distanceFromLocation:myLocation];
CLLocationDistance distanceB = [participantBLocation distanceFromLocation:myLocation];
if (distanceA < distanceB) {
return NSOrderedAscending;
} else if (distanceA > distanceB) {
return NSOrderedDescending;
} else {
return NSOrderedSame;
}
}];
Thank you!
NSArray *orderedUsers = [users sortedArrayUsingComparator:^(id a,id b) {
User *userA = (User *)a;
User *userB = (User *)b;
CLLocationDistance distanceA = [userA.location getDistanceFromLocation:myLocation];
CLLocationDistance distanceB = [userB.location getDistanceFromLocation:myLocation];
if (distanceA < distanceB) {
return NSOrderedAscending
} else if (distanceA > distanceB) {
return NSOrderedDescending;
} else {
return NSOrderedSame;
}
}];
First thing, you will need to calculate the distance between your current location and the location of each other user.
Talking mathematically, here is a Wolfram|Alpha example
Now "programmatic-ally", you can use CLLocation class, here is an example:
(CLLocationDistance)getDistanceFrom:(const CLLocation *)location
But first you will need to create the location object from your Latitude and Longitude. You can use:
(id)initWithLatitude:(CLLocationDegrees)latitude longitude:(CLLocationDegrees)longitude
You can calculate the distance (geographical, not flat plane!) between your position and each of these items' positions and order by that value.
- (void)viewDidLoad {
[super viewDidLoad];
// This Array Taken Globally
List_of_locationsArray =[[NSMutableArray alloc]initWithObjects:
#{#"latitude" : #"17.415045",#"logitude":#"78.421424"} ,#{#"latitude" : #"17.415045",#"logitude":#"78.421424"},#{#"latitude" : #"17.415045",#"logitude":#"78.421424"},#{#"latitude" : #"17.415045",#"logitude":#"78.421424"},#{#"latitude" : #"17.415045",#"logitude":#"78.421424"}
,nil];
}
-(void)sortingLocationsArray{
// CLLocation* currentLocation =[[CLLocation alloc]initWithLatitude:[currentLatitude doubleValue] longitude:[currentLogitude doubleValue]];
CLLocation* currentLocation =[[CLLocation alloc]initWithLatitude: currentLatitudeHere longitude:CurrentLogHere];
NSMutableArray* tempLocationsArr = [[NSMutableArray alloc]initWithCapacity:[locationsArray count]];
for (int i=0; i<[locationsArray count]; i++) {
CLLocationDegrees latValue = [[locationsArray[i] objectForKey:#"latitude"] doubleValue];
CLLocationDegrees longValue = [[locationsArray[i] objectForKey:#"logitude"] doubleValue];
CLLocation* location = [[CLLocation alloc]initWithLatitude:latValue longitude:longValue];
[tempLocationsArr addObject:location];
NSArray* sortLocationArry = [tempLocationsArr sortedArrayUsingComparator:^NSComparisonResult(CLLocation* location1, CLLocation* location2) {
CLLocationDistance distA = [location1 distanceFromLocation:currentLocation];
CLLocationDistance distB = [location2 distanceFromLocation:currentLocation];
if (distA < distB) {
return NSOrderedAscending;
} else if ( distA > distB) {
return NSOrderedDescending;
} else {
return NSOrderedSame;
}
}];
//ArrayAfterSorting is another mutable Array to Store Sorting Data
[ArrayAfterSorting removeAllObjects];
[sortLocationArry enumerateObjectsUsingBlock:^(CLLocation* location, NSUInteger idx, BOOL *stop) {
NSMutableDictionary *tempDict = [[NSMutableDictionary alloc]init];
[tempDict setObject:[NSString stringWithFormat:#"%f",location.coordinate.latitude] forKey:#"latitude"];
[tempDict setObject:[NSString stringWithFormat:#"%f",location.coordinate.longitude] forKey:#"logitude"];
[ArrayAfterSorting addObject:tempDict];
}];
NSLog(#"sortedArray : %#", ArrayAfterSorting);
}
}
You may solve your problem in following way
1)Firstly store all above values in separate array Like latArray ,longArray,nameArray
2)Now get the distance between location(longArray,latArray) from your current location.
Then store these distances in separate Array(distanceArray).
//getDistance Between currentLocation and desired Location
-(void *)getDistanceFromCurrentLocation{
for(int val=0;val<[latArray count];val++){
NSString *dis_From_Current_Location;
dis_From_Current_Location =nil;
CGFloat str_Longitude=[[defaults objectForKey:#"long"]floatValue];
CGFloat str_Latitude=[[defaults objectForKey:#"lati"]floatValue];
//Suppose this is your current location
CGFloat lat1= [[latArray objectAtIndex:val]floatValue];
//these array for lat
CGFloat long1=[[longArray objectAtIndex:val]floatValue];
//these array for longArray
CLLocation *location1 = [[CLLocation alloc] initWithLatitude:lat1 longitude:long1];
CLLocation *location2 = [[CLLocation alloc] initWithLatitude:str_Latitude longitude:str_Longitude];
CLLocationDistance dist=[location1 distanceFromLocation:location2];
NSLog(#"Distance i meters: %f", [location1 distanceFromLocation:location2]);
long long v = llabs(dist/1000);
dis_From_Current_Location=[NSString stringWithFormat:#"%lld km",v];
[location1 release];
[location2 release];
[distanceArray addObject: dis_From_Current_Location];
//distanceArray is Global NsMutableArray.
}
}
Now You should Apply sorting method(selection, bubble) fro sorting the distances.
One thing need to care is that when you sort the distanceArray
please adjust values of nameArray as according to the distanceArray
See Below code for sorting the distanceArray and adjust the nameArray's value.
-(void)getSoretdArray{
NSString * tempStr,*tempStr2;
for(int i=0;i<[distanceArray count]; i++){
for(int j=i+1;j<[distanceArray count]; j++){
if([distanceArray objectAtIndex:j]>[distanceArray objectAtIndex:j+1]){
tempStr=[distanceArray objectAtIndex:j];
NSString* str= [distanceArray objectAtIndex:j+1];
[ distanceArray insertObject:str atIndex:j];
[distanceArray insertObject:tempStr atIndex:j+1] ;
//also change the name of corresponding location.
//you have to adjust the stored names in namArray for storing names of Corresponding Distances
tempStr2=[nameArray objectAtIndex:j];
NSString* str1= [nameArray objectAtIndex:j+1];
[ nameArray insertObject:str1 atIndex:j];
[nameArray insertObject:tempStr2 atIndex:j+1] ;
}
}
}
}
This will definitely work just try to use carefully

NSMutableArray seems to be prematurely releasing

I'm trying to add Annotations to an array to place multiple pins on a map. I have everything in a for loop. The first time it loops through, it adds the object to the array just fine. When it goes back through... the array has 0 objects in it. Can anyone tell me why?
EDIT: I'm using ARC.
-(void)plotMultipleLocs {
float latitude;
float longitude;
NSRange commaIndex;
NSString *coordGroups;
for (int i=0; i<=cgIdArray.count; i++) {
coordGroups = [cgInAreaArray objectAtIndex:i];
commaIndex = [coordGroups rangeOfString:#","];
latitude = [[coordGroups substringToIndex:commaIndex.location] floatValue];
longitude = [[coordGroups substringFromIndex:commaIndex.location + 1] floatValue];
CLLocationCoordinate2D loc = CLLocationCoordinate2DMake(latitude, longitude);
MKCoordinateRegion reg = MKCoordinateRegionMakeWithDistance(loc, 1000, 1000);
self->mapView.region = reg;
MKPointAnnotation* ann = [[MKPointAnnotation alloc] init];
ann.coordinate = loc;
ann.title = [cgNameArray objectAtIndex:i];
ann.subtitle = [cgLocArray objectAtIndex:i];
NSMutableArray *mutAnnArray = [NSMutableArray arrayWithArray:annArray];
[mutAnnArray addObject:ann];
}
}
You are creating a mutable array within the loop and adding your object to it.
At the next iteration of the loop, you create a new mutable array and add a new annotation to it.
Leave aside the fact that you are creating it from another array rather than just adding your annotation to annArray
Basically, the array that you are adding objects to last as long as one iteration, and then goes out of scope.
Try moving the array out of the loop:
-(void)plotMultipleLocs {
float latitude;
float longitude;
NSRange commaIndex;
NSString *coordGroups;
NSMutableArray *mutAnnArray = [NSMutableArray arrayWithArray:annArray]; // Create one array outside the loop.
for (int i=0; i<=cgIdArray.count; i++) {
coordGroups = [cgInAreaArray objectAtIndex:i];
commaIndex = [coordGroups rangeOfString:#","];
latitude = [[coordGroups substringToIndex:commaIndex.location] floatValue];
longitude = [[coordGroups substringFromIndex:commaIndex.location + 1] floatValue];
CLLocationCoordinate2D loc = CLLocationCoordinate2DMake(latitude, longitude);
MKCoordinateRegion reg = MKCoordinateRegionMakeWithDistance(loc, 1000, 1000);
self->mapView.region = reg;
MKPointAnnotation* ann = [[MKPointAnnotation alloc] init];
ann.coordinate = loc;
ann.title = [cgNameArray objectAtIndex:i];
ann.subtitle = [cgLocArray objectAtIndex:i];
[mutAnnArray addObject:ann]; // Add the annotation to the single array.
}
// mutAnnArray will go out of scope here, so maybe return it, or assign it to a property
}
Have you tried retaining the instance to avoid it being released?
Every time through the loop, you create a new mutable array with the contents of a different array. The mutable array containing the object you added on the previous iteration is not kept.

Adding annotation on Map by parser latitude & longitude values

I have the following code:
MKCoordinateRegion region;
MKCoordinateSpan span;
span.latitudeDelta=0.0005;
span.longitudeDelta=0.0005;
CLLocationCoordinate2D location = mapView.userLocation.coordinate;
for (int i = 0; i < [appDelegate.markers count]; i++) {
marker *aMarker = [appDelegate.markers objectAtIndex:i];
location.latitude = [[aMarker.lat objectAtIndex:i] floatValue];
location.longitude =[[aMarker.lng objectAtIndex:i] floatValue];
region.span=span;
region.center=location;
if(addAnnotation != nil)
{
[mapView removeAnnotation:addAnnotation];
[addAnnotation release];
addAnnotation = nil;
}
addAnnotation = [[AddressAnnotation alloc] initWithCoordinate:location];
[mapView addAnnotation:addAnnotation];
}
I have parsed latitude and longitude in my XMLparser class. Now I want to add annotation on buttonclick event on Map. Can someone correct my code?
The warning NSString may not respond to -objectAtIndex means lat and lng are NSString objects (which don't have an objectAtIndex method).
You don't need to call objectAtIndex on lat and lng--they are the values in the specific marker object you just retrieved from the array.
(By the way, you get the warning when you compile the code--not when you "run" it like in your comment. Secondly, don't ignore compiler warnings. In this case, when you run the code you will get an exception: NSInvalidArgumentException - unrecognized selector.)
The other problem is the loop removes the previously added annotation before adding the current one. If you want to add all the markers to the map, this doesn't make sense. Currently, it would end up adding only the last marker to the map. If you really want to add only the last marker then you don't need to loop through the array (just grab the last marker from the array directly and create an annotation from it).
If instead you do want to add all the markers to the map, the loop should look like this:
for (int i = 0; i < [appDelegate.markers count]; i++)
{
marker *aMarker = [appDelegate.markers objectAtIndex:i];
location.latitude = [aMarker.lat floatValue];
location.longitude =[aMarker.lng floatValue];
AddressAnnotation *addrAnnot = [[AddressAnnotation alloc] initWithCoordinate:location];
[mapView addAnnotation:addrAnnot];
[addrAnnot release];
}
It's not clear what you're doing with region. If you're trying to set the region so that the map view shows all the annotations, look at the answers to this question: iOS MKMapView zoom to show all markers.

CLLocationCoordinate2D without knowing how many will be in the array?

I need to build an array using something like the following:
CLLocationCoordinate2D points[4];
points[0] = CLLocationCoordinate2DMake(41.000512, -109.050116);
points[1] = CLLocationCoordinate2DMake(41.002371, -102.052066);
points[2] = CLLocationCoordinate2DMake(36.993076, -102.041981);
points[3] = CLLocationCoordinate2DMake(36.99892, -109.045267);
Problem is, I will never know how many items are going to be in the array, so I can specify the count. Is there a way to create a CLLocationCoordinate2D array, and just insert new coordinates without knowing what the final total will be?
EDIT: My final goal is to use the coordinates to make an MKPolyline, using the polylineWithCoordinates method which needs a CLLocationCoordinate2D array.
// unpacking an array of NSValues into memory
CLLocationCoordinate2D *points = malloc([mutablePoints count] * sizeof(CLLocationCoordinate2D));
for(int i = 0; i < [mutablePoints count]; i++) {
[[mutablePoints objectAtIndex:i] getValue:(points + i)];
}
MKPolyline *myPolyline = [MKPolyline polylineWithCoordinates:points count:[mutablePoints count]];
free(points);
Box them up in an NSValue object and throw them in an NSMutableArray.