NSMutableArray parsing csv not working? - ios

I have this code where I use NSMutableArray to parse a csv file. There are no errors that stop me from running the app however the map doesn't display anything.
NSString *csvFilePath = [[NSBundle mainBundle] pathForResource:#"Data2" ofType:#"csv"];
NSString *dataStr = [NSString stringWithContentsOfFile:csvFilePath encoding:NSUTF8StringEncoding error:nil];
NSMutableArray *allLinedStrings = [[NSMutableArray alloc]initWithArray:[dataStr componentsSeparatedByString:#"\r"]];
NSMutableArray *latitude = [[NSMutableArray alloc]init];
NSMutableArray *longitude = [[NSMutableArray alloc]init];
NSMutableArray *description = [[NSMutableArray alloc]init];
NSMutableArray *address = [[NSMutableArray alloc]init];
NSMutableArray *temperature = [[NSMutableArray alloc]init];
NSMutableArray *time = [[NSMutableArray alloc]init];
NSMutableArray *ambient = [[NSMutableArray alloc]init];
NSMutableArray *filteredLocations = [NSMutableArray array];
MKMapPoint* pointArr = malloc(sizeof(MKMapPoint) * filteredLocations.count);
for (int idx = 0; idx < [allLinedStrings count]; idx++)
{
NSMutableArray *infos = [[NSMutableArray alloc]initWithArray:[[allLinedStrings objectAtIndex:idx] componentsSeparatedByString:#","]];
if ([infos count] > 1)
{
[latitude addObject:[infos objectAtIndex:4]];
[longitude addObject:[infos objectAtIndex:5]];
[description addObject:[infos objectAtIndex:0]];
[address addObject:[infos objectAtIndex:10]];
[temperature addObject:[infos objectAtIndex:6]];
[time addObject:[infos objectAtIndex:15]];
[ambient addObject:[infos objectAtIndex:8]];
if([[latitude objectAtIndex:4] isEqualToString:#"NULL"] || [[longitude objectAtIndex:5] isEqualToString:#"NULL"] || [[description objectAtIndex:0] isEqualToString:#"NULL"] || [[address objectAtIndex:10]isEqualToString:#"NULL"] || [[temperature objectAtIndex:6] isEqualToString:#"NULL"] || [[time objectAtIndex:15]isEqualToString:#"NULL"] || [[ambient objectAtIndex:8] isEqualToString:#"NULL"]) {continue;}
CLLocationCoordinate2D coordinate;
coordinate.latitude = [[latitude objectAtIndex:4] doubleValue];
coordinate.longitude = [[longitude objectAtIndex:5] doubleValue];
Location *annotation = [[Location alloc] initWithName:[description objectAtIndex:0] address:[address objectAtIndex:10] temperature:[temperature objectAtIndex:6] time:[time objectAtIndex:15] ambient:[ambient objectAtIndex:8] coordinate:coordinate] ;
[mapview addAnnotation:annotation];
[filteredLocations addObject:annotation];
MKMapPoint point = MKMapPointForCoordinate(coordinate);
pointArr[idx] = point;
}
}
self.routeLine = [MKPolyline polylineWithPoints:pointArr count:filteredLocations.count];
[self.mapview addOverlay:self.routeLine];
free(pointArr);
MKMapRect zoomRect = MKMapRectNull;
for (id <MKAnnotation> annotation in mapview.annotations)
{
MKMapPoint annotationPoint = MKMapPointForCoordinate(annotation.coordinate);
MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.1, 0.1);
zoomRect = MKMapRectUnion(zoomRect, pointRect);
}
[mapview setVisibleMapRect:zoomRect animated:YES];
self.mapview.delegate = self;
}
I guess there must be something wrong with how I'm calling the objects or maybe the MKMapPoint but I don't manage to find what's blocking the app from displaying the data. I've tried using both "initWithObjects" and removing "if ([infos count] > 1){" but when ran it crashed showing a breakdown point in "NSMutableArray *latitude = [[NSMutableArray alloc]init];".

Based on your previous questions about this project, you want to do the following at a high level:
Parse a CSV file where each line has coordinate data. Ignore lines that have "null" data. (For the purpose of this answer, let's ignore that one could use a pre-built CSV parser, or use a different format altogether.)
Show annotations for lines with "good" data.
Connect all the annotations with a line.
For requirement 1 (R1), you already know how to load the CSV file, loop through the lines, and identify the lines with "null" data.
For requirement 2 (R2), after some research, you know that you can create and add annotations to the map one at a time and the map doesn't need to know ahead of time how many you will add so that means the first two requirements could be done in the same loop.
For requirement 3 (R3), after some research, you know that to create and add a polyline to the map, you need to know ahead of time how many points will be in the line.
For R1 and R2, you will be looping through the lines of the CSV and identify the non-null lines.
So that means you will know how many points will be in the polyline after the loop that handles R1 and R2. That means the polyline must be created after that loop.
But to create the polyline, you need not just the point count but the coordinates for each point as well.
That means while looping through the lines in the CSV, you need to save the coordinate data somewhere (in the same order it appeared in the CSV).
In Objective-C, a convenient structure that allows you to add data to it without knowing in advance how many objects will be added is an NSMutableArray.
So now we have this very high-level plan:
Loop through the CSV file, ignore lines with null data, create and add annotations, add the line data to an NSMutableArray (NSMA).
Create a polyline using the point data in NSMA, add the polyline to the map.
With this plan, we see we need one NSMutableArray. Notice that in the existing code, you have a Location class that holds (or could hold) all the data from each line of the CSV.
That means we could simply add these Location objects to the NSMA. NSMutableArrays can hold any type of object (they don't have to be just NSStrings).
So here's a slightly more detailed plan:
Initialize an NSMutableArray called filteredLocations (eg. NSMutableArray *filteredLocations = [NSMutableArray array];).
Loop through the CSV file, ignore lines with null data, create a Location object and add as an annotation, add the Location object to filteredLocations (eg. [filteredLocations addObject:annotation];).
Initialize (malloc) a C array to hold the points of the polyline with the point count being the count of filteredLocations.
Loop through filteredLocations, add point from filteredLocations to the C array.
Create and add a polyline to the map.
In this plan note we have two separate loops: The first one is for R1 and R2. The second one is for R3.
If required, I will post sample code that implements this plan.
First, just to explain your latest NSRangeException error, it is happening on this line:
if([[latitude objectAtIndex:4] isEqualToString:#"NULL"] || ...
because you've declared latitude as an array and the first time the if executes in the loop, latitude only has one object (a few lines above this if you do [latitude addObject:...). The index of an array starts at zero so the bounds of an array with one object are zero to zero hence the error message saying index 4 beyond bounds [0 .. 0].
There are many other issues with the rest of the code.
There is not enough room in this answer to explain in detail.
I urge you, if possible, to stop, step back and re-start with a much simpler project or tutorials and, most importantly, learn the absolute basics of programming in general.
Here is an example of code that should work based on your sample data:
-(void)viewDidLoad
{
[super viewDidLoad];
self.mapview.delegate = self;
NSString *csvFilePath = [[NSBundle mainBundle] pathForResource:#"Data2" ofType:#"csv"];
NSString *dataStr = [NSString stringWithContentsOfFile:csvFilePath encoding:NSUTF8StringEncoding error:nil];
NSArray *allLinedStrings = [dataStr componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSMutableArray *filteredLocations = [NSMutableArray array];
for (int idx = 0; idx < [allLinedStrings count]; idx++)
{
NSArray *infos = [[allLinedStrings objectAtIndex:idx] componentsSeparatedByString:#","];
if ([infos count] > 15)
{
NSString *latitude = [infos objectAtIndex:4];
NSString *longitude = [infos objectAtIndex:5];
NSString *description = [infos objectAtIndex:0];
NSString *address = [infos objectAtIndex:10];
NSString *temperature = [infos objectAtIndex:6];
NSString *time = [infos objectAtIndex:15];
NSString *ambient = [infos objectAtIndex:8];
if([latitude isEqualToString:#"NULL"]
|| [longitude isEqualToString:#"NULL"]
|| [description isEqualToString:#"NULL"]
|| [address isEqualToString:#"NULL"]
|| [temperature isEqualToString:#"NULL"]
|| [time isEqualToString:#"NULL"]
|| [ambient isEqualToString:#"NULL"])
{
continue;
}
CLLocationCoordinate2D coordinate;
coordinate.latitude = [latitude doubleValue];
coordinate.longitude = [longitude doubleValue];
Location *annotation = [[Location alloc] initWithName:description
address:address
temperature:temperature
time:time
ambient:ambient
coordinate:coordinate];
[mapview addAnnotation:annotation];
[filteredLocations addObject:annotation];
}
}
MKMapPoint* pointArr = malloc(sizeof(MKMapPoint) * filteredLocations.count);
for (int flIndex = 0; flIndex < filteredLocations.count; flIndex++)
{
Location *location = [filteredLocations objectAtIndex:flIndex];
MKMapPoint point = MKMapPointForCoordinate(location.coordinate);
pointArr[flIndex] = point;
}
self.routeLine = [MKPolyline polylineWithPoints:pointArr count:filteredLocations.count];
[self.mapview addOverlay:self.routeLine];
free(pointArr);
[self.mapview showAnnotations:self.mapview.annotations animated:YES];
}

Related

How to add new Annotation with existed annotations?

In my application, I was published recorded video from particular location. I can get the list of published location details from another service response. so Initially I got that published location details and displayed it on MapView. In MapView the pins are displayed with Cluster effect, so I used kingpin.
Here below I have loaded the Annotations on Map.
- (void)loadLocations:(NSArray *)arrayValues
{
_annotationArray = arrayValues;
[self.clusteringController setAnnotations:[self reSetannotations]];
[self.mapView setZoomEnabled:YES];
[self.mapView setCenterCoordinate:self.mapView.userLocation.coordinate];
[self.mapView setUserTrackingMode:MKUserTrackingModeFollow];
KPGridClusteringAlgorithm *algorithm = [KPGridClusteringAlgorithm new];
algorithm.annotationSize = CGSizeMake(25, 50);
algorithm.clusteringStrategy = KPGridClusteringAlgorithmStrategyTwoPhase;
self.clusteringController = [[KPClusteringController alloc] initWithMapView:self.mapView
clusteringAlgorithm:algorithm];
self.clusteringController.delegate = self;
self.clusteringController.animationOptions = UIViewAnimationOptionCurveEaseOut;
[self.clusteringController setAnnotations:[self annotations]];
NSString * lastobjlat;
double miles;
CLLocation * location = [COMMON currentLocation];
lastobjlat = [NSString stringWithFormat:#"%f",location.coordinate.latitude];
miles = 1.;
double scalingFactor = ABS( (cos(2 * M_PI * [lastobjlat floatValue] / 360.0) ));
MKCoordinateSpan span;
span.latitudeDelta = miles/69.0;
span.longitudeDelta = miles/(scalingFactor * 69.0);
MKCoordinateRegion region;
region.span = span;
region.center = location.coordinate;
[self.mapView setRegion:region animated:YES];
self.mapView.showsUserLocation = YES;
//Call in the below selectAnnotationAction method when I came to this, after I published new one on existed or new locations
if (COMMON.isRecentPublication == YES) {
COMMON.isRecentPublication = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self selectAnnotationAction];
});
}
}
//To reset the Annotations
- (NSArray *)reSetannotations
{
NSMutableArray *annotations = [NSMutableArray array];
return annotations;
}
//Here I managed location details on my custom marker class MapAnnotation.
- (NSArray *)annotations {
CLLocationCoordinate2D locationPort;
NSMutableArray *annotations = [NSMutableArray array];
NSString *latitude, *longitude;
if ([_annotationArray count] > 0) {
for (int i = 0; i <[_annotationArray count]; i++){
latitude = [NSString stringWithFormat:#"%#",[[_annotationArray objectAtIndex:i] valueForKey:#"latitude"]];
latitude = [latitude stringByReplacingOccurrencesOfString:#"\n" withString:#""];
latitude = [latitude stringByReplacingOccurrencesOfString:#"\t" withString:#""];
longitude = [NSString stringWithFormat:#"%#",[[_annotationArray objectAtIndex:i] valueForKey:#"longitude"]];
longitude = [longitude stringByReplacingOccurrencesOfString:#"\n" withString:#""];
longitude = [longitude stringByReplacingOccurrencesOfString:#"\t" withString:#""];
latitude = [NSString replaceEmptyStringInsteadOfNull:latitude];
longitude = [NSString replaceEmptyStringInsteadOfNull:longitude];
int publicationRatio = [[[_annotationArray objectAtIndex:i] valueForKey:#"publicationRatio"] intValue];
int publicationCount = [[[_annotationArray objectAtIndex:i] valueForKey:#"publicationsCount"] intValue];
int teazLocationId = [[[_annotationArray objectAtIndex:i] valueForKey:#"objectId"] intValue];
BOOL isUpgrade = [[[_annotationArray objectAtIndex:i] valueForKey:#"isUpgraded"] boolValue];
locationPort = CLLocationCoordinate2DMake([latitude doubleValue] ,
[longitude doubleValue]);
//TODO : This is my custom annotation method
MapAnnotation *a1 = [[MapAnnotation alloc] initWithCoordinate:locationPort
tag:i
publicationRatio:publicationRatio
publicationCount:publicationCount
teazLocationId:teazLocationId isUpgraded:isUpgrade];
a1.itemindex = i + 1;
a1.publicationRatio = publicationRatio;
a1.publicationCount = publicationCount;
a1.teazLocationId = teazLocationId;
a1.isUpgraded = isUpgrade;
a1.coordinate = CLLocationCoordinate2DMake([latitude doubleValue] ,
[longitude doubleValue] );
[annotations addObject:a1];
if (COMMON.isRecentPublication == YES) {
if ([COMMON.recentPublicationLocationID isEqual: #(publishedLocationId)]) {
_recentAnnotationView = [[MKPinAnnotationView alloc] initWithAnnotation:a1 reuseIdentifier:#"cluster"];
}
}
}
}
return annotations;
}
It's not a problem to load Annotations with clustering effect on initial time. But when I published some one from same or different(new also) locations, I need to redirect Map screen(publishing section is other screen) and I need to display that publication detail with Active pin image.
Following steps I made to show the published location detail on Map
I created recentPublicationLocationID as a global variable and store the recentPublishedLocationID from response after published service.
Then this return type method - (NSArray *)annotations(after redirect to mapView I got the location details from another webservice after that it will called),
I have compared with recentPublishedId If existed or not. Then If existed, I have assigned my custom annotation (contains the recentPublished location details) to global MKPinAnnotationView instance - _recentAnnotationView
Then I directly called the didSelectPinAnnotation delegate method from this method - (void)loadLocations:(NSArray *)arrayValues like below,
//Pass recent published location details
if (COMMON.isRecentPublication == YES)
{
COMMON.isRecentPublication = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self selectAnnotationAction];
});
}
//selectAnnotationAction
- (void)selectAnnotationAction {
COMMON.isRecentPublication = NO;
COMMON.recentPublicationLocationID = nil;
[self mapView:self.mapView didSelectAnnotationView:_recentAnnotationView];
}
If I directly passed recentPublishedLocation details to didSelectAnnotationView delegate, I can only show the In Active pin instead of Active pin.
Then I debug with breakpoint why I can see the In active pin only ?
Because In this situation the didselect delegate was called and I can see the Active pin image. But it's only within sec.
Because viewForAnnotation delegate was called quickly for other pin annotations so the selected one goes to unselected state
This is the real problem. How can I overcome this work with clusters ?
Because when I displayed that published location detail correctly on map even it should be work with clustering effect. Yes I will zoom back to see the pins with cluster effect .
Finally I got the solution and achieved my requirement using ClusterKit library instead of Kingpin.
This tool really helpful me against possible to achieve append annotations and customize every thing adopt with my requirements.
So it's more helpful to me. Of course all of you.
And this tool supports Apple & Goole maps with Objective c as well as Swift.
I hope this one more helpful to other developers also.
Thanks :)

Thread 1 exc_bad_access Error

I am developing an iOS app and working with google maps. Drawing lines on a map, add the coordinates in a mutableArray, save those coordinate in NSUSERDEFAULT works perfectly. But when getting the data back from NSUSERDEFAULT and showing the coordinates on google maps does not work. In GMSMutablePath, the app crashes with the following error message : exec_bad_access (code=1,address=0*4). Any idea why?
Here is the related code:
Getting Data
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
NSArray *coordinateArray = [defaults objectForKey:#"numberArray"];
NSLog(#"%#",coordinateArray
for (int i = 0; i < [coordinateArray count]; i++) {
NSDictionary *dic = [coordinateArray objectAtIndex:i];
NSString *lat = [dic valueForKey:#"lat"];
NSString *longitude = [dic valueForKey:#"long"];
_valuePoints.latitude = [lat doubleValue];
_valuePoints.longitude = [longitude doubleValue];
GMSMutablePath *path = [[GMSMutablePath alloc] initWithPath:self.polyline.path];
[path addCoordinate:_valuePoints];
self.polyline.path = path;
}

Remove duplicate member from CSV file Contant and store in array?

i have an csv file having some duplicate member, i want to delete duplicate and store sort values in an array, i am using following procedure , but its don't give me unique array.
here is my csv value file content
37.46768876,-122.29178052
37.46797307,-122.29193961
37.46825147,-122.29210281
37.46852845,-122.29226818
37.46878976,-122.29244219
37.46905295,-122.29263363
37.46930994,-122.29282659
37.46956224,-122.2930368
37.46980661,-122.29324794
37.47004943,-122.29347317
37.47029083,-122.29370551
37.46768876,-122.29178052
37.46797307,-122.29193961
37.46825147,-122.29210281
37.46852845,-122.29226818
37.46878976,-122.29244219
37.46905295,-122.29263363
37.46930994,-122.29282659
37.46956224,-122.2930368
37.46980661,-122.29324794
37.47004943,-122.29347317
37.47029083,-122.29370551
37.47052113,-122.29393953
37.47074882,-122.29417431
37.47098645,-122.29441454
37.47121917,-122.29465652
37.47146019,-122.2948939
37.47169204,-122.29512775
37.47193243,-122.29536731
37.47216892,-122.29559597
37.4724045,-122.29584441
37.47263575,-122.29607826
37.47287359,-122.29631245
37.47310992,-122.29655586
37.47335094,-122.29678896
37.47358589,-122.2970276
37.47382733,-122.29725927
37.47407652,-122.29748533
37.47432651,-122.29770016
37.47457788,-122.29788741
37.47484795,-122.29807718
37.47511504,-122.29823928
37.46768876,-122.29178052
37.46797307,-122.29193961
37.46825147,-122.29210281
37.46852845,-122.29226818
37.46878976,-122.29244219
37.46905295,-122.29263363
37.46930994,-122.29282659
37.46956224,-122.2930368
37.46980661,-122.29324794
37.47004943,-122.29347317
37.47029083,-122.29370551
37.47052113,-122.29393953
37.47074882,-122.29417431
37.47098645,-122.29441454
37.47121917,-122.29465652
37.47146019,-122.2948939
37.47169204,-122.29512775
37.47193243,-122.29536731
37.47216892,-122.29559597
37.4724045,-122.29584441
37.47263575,-122.29607826
37.47287359,-122.29631245
37.47310992,-122.29655586
37.47335094,-122.29678896
37.47358589,-122.2970276
37.47382733,-122.29725927
37.47407652,-122.29748533
37.47432651,-122.29770016
37.47457788,-122.29788741
37.47484795,-122.29807718
37.47511504,-122.29823928
37.47539512,-122.29839703
37.47567109,-122.29853676
37.47596509,-122.29866961
37.47624844,-122.2987909
37.47654113,-122.29890078
37.47684146,-122.29899726
37.47714065,-122.29908334
37.47744621,-122.29914235
37.47775391,-122.29919189
37.47805503,-122.29923078
37.46768876,-122.29178052
37.46797307,-122.29193961
37.46825147,-122.29210281
37.46852845,-122.29226818
37.46878976,-122.29244219
37.46905295,-122.29263363
37.46930994,-122.29282659
37.46956224,-122.2930368
37.46980661,-122.29324794
37.47004943,-122.29347317
37.47029083,-122.29370551
37.47052113,-122.29393953
37.47074882,-122.29417431
37.47098645,-122.29441454
37.47121917,-122.29465652
37.47146019,-122.2948939
37.47169204,-122.29512775
37.47193243,-122.29536731
37.47216892,-122.29559597
37.4724045,-122.29584441
37.47263575,-122.29607826
37.47287359,-122.29631245
37.47310992,-122.29655586
37.47335094,-122.29678896
37.47358589,-122.2970276
37.47382733,-122.29725927
37.47407652,-122.29748533
37.47432651,-122.29770016
37.47457788,-122.29788741
37.47484795,-122.29807718
37.47511504,-122.29823928
37.47539512,-122.29839703
37.47567109,-122.29853676
37.47596509,-122.29866961
37.47624844,-122.2987909
37.47654113,-122.29890078
37.47684146,-122.29899726
37.47714065,-122.29908334
37.47744621,-122.29914235
37.47775391,-122.29919189
37.47805503,-122.29923078
37.47836852,-122.29925626
37.47867584,-122.29926607
37.47898412,-122.29925944
37.47928265,-122.29924209
37.47959504,-122.29920555
37.47990379,-122.2991675
37.4802021,-122.29911704
37.48050104,-122.29907018
37.48081016,-122.29901151
37.48112252,-122.29896314
37.48143034,-122.2989116
37.48173368,-122.29885946
37.48204679,-122.298808
37.48235759,-122.2987603
37.48267635,-122.29871948
37.48296448,-122.29865444
37.48327373,-122.29860566
37.48356664,-122.29855578
37.48386847,-122.29850608
My code...
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,
YES);
NSString *docDirectory = [paths objectAtIndex:0];
NSString *outputFileName = [docDirectory stringByAppendingPathComponent:#"People.csv"];
NSString* fileContents = [NSString stringWithContentsOfFile:outputFileName encoding:nil error:nil];
NSArray* pointStrings = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"point string=%#",pointStrings);
NSMutableArray* points = [[NSMutableArray alloc] initWithCapacity:pointStrings.count];
for(int idx = 0; idx < pointStrings.count-1; idx++)
{
// break the string down even further to latitude and longitude fields.
NSString* currentPointString = [pointStrings objectAtIndex:idx];
NSArray* latLonArr = [currentPointString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#","]];
CLLocationDegrees latitude = [[latLonArr objectAtIndex:0] doubleValue];
CLLocationDegrees longitude = [[latLonArr objectAtIndex:1] doubleValue];
CLLocation* currentLocation = [[[CLLocation alloc] initWithLatitude:latitude longitude:longitude] autorelease];
[points addObject:currentLocation];
}
NSMutableArray *pointsunique = [NSMutableArray array];
for (id obj in points) {
if (![pointsunique containsObject:obj]) {
[pointsunique addObject:obj];
}
}
//sortlocationArray = [NSOrderedSet orderedSetWithArray:locationArray];
NSLog(#"pointsunique count %d",[pointsunique count]);//same count,expect less then points count
NSLog(#"temp1 count %d",[points count]);//same count
NSLog(#"pointsunique %#",pointsunique);

Attempting to drop pins based on MKMap from values from array

As the question says I am trying to add pins to my map based on the coordinates returned by my php file. Said file returns the following results
[{"dogid":"1","latitude":"15.435786","longitude":"-21.318447"},{"dogid":"1","latitude":"14.00000","longitude":"-18.536711"}]
What I am doing (well I believe i am) is taking the values from the link and saving them to a string. Secondly, save that string value to an array. Then, I go thru this array and save out the latitude and longitude and assign it to CLLocationCordinate 2dcoord. After whch I expect both pins to be dropped on whatever location they received.
However, what occurs is: Upon running the program, when it arrives on this lin
for (NSDictionary *row in locations) {
the loop is not run to assign the values, and it jumps to the end. Oddly, a single pin is dropped on the map (thou location doesnt appear to be the values that it waas passed).
Would appreciate a little incite into the matter.
Thanks
- (void)viewDidAppear:(BOOL)animated
{
NSMutableArray *annotations = [[NSMutableArray alloc] init];
NSURL *myURL =[NSURL URLWithString:#"link.php"];
NSError *error=nil;
NSString *str=[NSString stringWithContentsOfURL:myURL encoding:NSUTF8StringEncoding error:&error];
CLLocationCoordinate2D coord;
NSArray *locations=[NSArray arrayWithContentsOfFile:str];
for (NSDictionary *row in locations) {
NSNumber *latitude = [row objectForKey:#"latitude"];
NSNumber *longitude = [row objectForKey:#"longitude"];
// NSString *title = [row objectForKey:#"title"];
//Create coordinates from the latitude and longitude values
coord.latitude = latitude.doubleValue;
coord.longitude = longitude.doubleValue;
}
MKPointAnnotation *pin = [[MKPointAnnotation alloc] init];
pin.coordinate = coord;
[self.mapView addAnnotation:pin];
}
It looks like you are trying to save api response to and Array.
Api always returns json string which is NSString.
You need to convert decode json string.
In your case
NSString *str=[NSString stringWithContentsOfURL:myURL encoding:NSUTF8StringEncoding error:&error];
you need to decode str with [NSJSONSerialization JSONObjectWithData:<#(NSData )#> options:<#(NSJSONReadingOptions)#> error:<#(NSError *)#>] which give you proper array of dictionary.
Hope it will help you

How do you prevent sortedArrayUsingSelector from removing duplicate entries (ios)?

How can I use the following method to sort my array and keep the duplicates? All I want is to sort the distance array and have the lineItems array sort in the same order so that my line items are sorted by distance. Is there an easy way to do this? I've tried many different implementations with no luck.
lineItems = [[NSMutableArray alloc] initWithArray:(NSMutableArray *)[data objectForKey:#"line_items"]];
distanceArray = [[NSMutableArray alloc] initWithCapacity:[lineItems count]];
for (int i = 0; i < [lineItems count]; i++) {
CLLocation *spotLocation = [[CLLocation alloc] initWithLatitude:[[[lineItems objectAtIndex:i] objectForKey:#"latitude"] floatValue] longitude:[[[lineItems objectAtIndex:i] objectForKey:#"longitude"] floatValue]];
CLLocationDistance distance = ([myLocation distanceFromLocation:spotLocation] / 1000) * 0.621371192;
NSNumber *foo = [[NSNumber alloc] initWithDouble:distance];
[distanceArray insertObject:foo atIndex:i];
}
My bubble sort implementation:
for (int i=0;i<[distanceArray count]-1;i++){
for(int j=1;j<[distanceArray count];j++){
if ([[distanceArray objectAtIndex:i]doubleValue] >[[distanceArray objectAtIndex:j]doubleValue]){
NSNumber *temp_i = [[NSNumber alloc] initWithDouble:[[distanceArray objectAtIndex:i]doubleValue]];
NSNumber *temp_j = [[NSNumber alloc] initWithDouble:[[distanceArray objectAtIndex:j]doubleValue]];
[distanceArray removeObjectAtIndex:i];
[distanceArray removeObjectAtIndex:j];
[distanceArray insertObject:temp_j atIndex:i];
[distanceArray insertObject:temp_i atIndex:j];
NSDictionary *tempObj_i = [[NSDictionary alloc] initWithDictionary:[lineItems objectAtIndex:i]];
NSDictionary *tempObj_j = [[NSDictionary alloc] initWithDictionary:[lineItems objectAtIndex:j]];
[lineItems removeObjectAtIndex:i];
[lineItems removeObjectAtIndex:j];
[lineItems insertObject:tempObj_j atIndex:i];
[lineItems insertObject:tempObj_i atIndex:j];
}
}
}
Your problem isn't -sortedArrayUsingSelector:, it's NSDictionary's -allKeys method. Keys are unique in a dictionary, so the -allKeys array won't have any duplicates.
Use a separate NSArray instance to store the result of the sort, or use inafziger's suggestion.
Instead of using your sort function, try this:
[distanceArray sortArrayUsingSelector:#selector(compare:)];
Then you don't need the dictionary at all.
why not just do an old fashion bubble sort?
something like this:
distanceArray=[NSMutableArray arrayWithOjects:[ distanceArray allKeys]];
for (int i=0;i<[distanceArray count]-1;i++){
for(int j=1;j<[distanceArray count];j++){
if ([[distanceArray objectAtIndex:i]floatValue] >[[distanceArray objectAtIndex:j]floatValue]){
id temp=[[distanceArray objectAtIndex:i]floatValue];
[distanceArray insertObject:[[distanceArray objectAtIndex:j]floatValue] atIndex:i];
[distanceArray insertObject:temp atIndex:j];
}
}
}
and they're all sorted with duplicates (it's not pretty but it works)
edit:
if nothing works..make a objective c object with something like: coord1, coord2, distance, and maybe an unique id ..so you never have a duplicate item .Declare them as property to that object and then bubble sort them (with maybe changing the id-s too) .Like if you have an object with id=2 and you move it in array on index 1, then change the id to match the index...or something to that effect.Then you wont need the dictionary cuz you have the coord in the object itself...it should be easier to do ..at least in my mind
I ended up just turning the lineItems into array of mutable dictionaries and adding the distance into each line item. Then using the sortUsingDescriptor method. I appreciate everyone's help! Something so simple was so difficult for me... thanks:
for (int i = 0; i < [lineItems count]; i++) {
CLLocation *spotLocation = [[CLLocation alloc] initWithLatitude:[[[lineItems objectAtIndex:i] objectForKey:#"latitude"] floatValue] longitude:[[[lineItems objectAtIndex:i] objectForKey:#"longitude"] floatValue]];
CLLocationDistance distance = ([myLocation distanceFromLocation:spotLocation] / 1000) * 0.621371192;
NSNumber *foo = [[NSNumber alloc] initWithDouble:distance];
NSMutableDictionary *newLineItem = [[lineItems objectAtIndex:i] mutableCopy];
[newLineItem setObject:foo forKey:#"distance"];
[lineItems removeObjectAtIndex:i];
[lineItems insertObject:newLineItem atIndex:i];
newLineItem = nil;
}
[lineItems sortUsingDescriptors:[NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:#"distance" ascending:YES]]];

Resources