I have a rather urgent problem that I can find no answer to.
I am trying to draw the path between current location and destination using google maps API in an iPhone application. I am getting the data from Google , parse the JSON with no errors , decode the polyline and then I have this method:
-(void) loadRouteWithPoints:(NSArray *) routes
{
CLLocationCoordinate2D coords[[routes count]];
for(int i = 0; i < routes.count; i++)
{
CLLocation* loc = (CLLocation*)[NSKeyedUnarchiver unarchiveObjectWithData:[routes objectAtIndex:i]];
CLLocationCoordinate2D c;
c.latitude = loc.coordinate.latitude;
c.longitude = loc.coordinate.longitude;
coords[i] = c;
}
MKPolyline *line = [MKPolyline polylineWithCoordinates:(CLLocationCoordinate2D*)coords count:[routes count]];
[self.mapsView addOverlay:line];
}
This throws an exception on [NSKeyedUnarchiver unarchiveObjectWithData:[routes objectAtIndex:i]]; saying this:
"-[CLLocation bytes]: unrecognized selector sent to instance"
The array that I send as a parameter is the one that I get after decoding the polyline and it's not nil , it has - in my test - 47 values. I can see them and each entry looks like this:
<+44.47874069,+26.10523033> +/- 0.00m (speed -1.00 mps / course -1.00) # 6/29/12 17:09:37 Eastern European Summer Time
Do you have any idea what might cause this crash and how could it be fixed?
the error here indicates that the value you think is in [routes objectAtIndex:x] may not be what you think it is.
try separating out the line, seeing what exactly the class kind is for the object retrieved at [routes objectAtIndex:x] and you'll probably have your answer.
it seems possible that the object is already a CLLocation and doesn't need to be unencoded …
Related
I have an array of locations
NSArray *locations = [[NSArray alloc]initWithObjects:loc1, loc2, loc3, loc4, loc5, loc6, loc7, loc8, loc9, nil];
I iterate through the array and store the different distances from my current location to loc1, loc2 and so on in another array, and sort them from nearest to furthest. That works like a charm.
However, I need to associate the distance with the name of the location. Ex: If loc5 is the nearest place, that should be on the top of the list. I know the names of the different locations, I just need to associate them somehow.
Any suggestions?
EDIT:
Taking your advice into consideration, I figured that I could do something like this:
I have an array with all the stored distances, let's call the array a.
NSMutableDictionary *dic = [[NSMutableDictionary alloc]initWithObjects:locations forKeys:a];
NSArray *sortedArray = [a sortedArrayUsingSelector:#selector(compare:)];
for(int i = 0; i <[sortedArray count]; i++){
CLLocation *loc = [dic valueForKey:[sortedArray objectAtIndex:i]];
NSLog(#"Lat: %f , Lng: %f" , loc.coordinate.latitude , loc.coordinate.longitude);
}
This way I will get the latitude and longitude for the different distances, which I can check against my locally stored coordinates. However this gives me an error saying:
-[NSCFNumber length]: unrecognized selector sent to instance 0x1891f0
* Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[NSCFNumber length]:
unrecognized selector sent to instance 0x1891f0'
And it points me to the line
CLLocation *loc = [dic valueForKey:[sortedArray objectAtIndex:i]];
Thread 1: Program received signal: "SIGABRT".
Any idea how I can fix this?
I would create an information holder object that holds all the data for your different locations (name, location, last calculated distance, etc.), put all those objects into an NSMutableArray, then sort it using the sortUsingSelector: or sortUsingComparator: methods on NSMutableArray.
The former would require you to define a selector on your info holder that returns an NSComparisonResult, while the latter (IIRC) allows you to define a block that does the same thing.
You could also theoretically store the names as keys of an NSDictionary, with the CLLocations as the values, then use the keysSortedByValueUsingComparator: method to determine the distance using a block that acts on the CLLocations and your reference location.
This would no longer require an explicit info holder, but you're liable to rack up a lot of processor time as you would need to calculate the distance from your reference location and TWO CLLocations for each run of the NSComparator block.
First implement these delegates - (assuming you have included coreLocation framework & have registered for location updates.
-(void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
// Handle location updates
}
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
// Handle error
}
The distance between two CLLocation points may be calculated by calling the distanceFromLocation: method of the end location and passing through the start location as an argument. For example, the following code calculates the distance between the points specified by newLocation and oldLocation:
CLLocationDistance distance = [newLocation distanceFromLocation:oldLocation];
Is it possible to get coordinates by giving name of a place in iPhone? I don't want to use any Web Service as they have few limitations. Can i do it using iPhone SDK?
Thanks in advance.
As #raj2raaz have mentioned, we can'nt get coordinates by just specify the name of the place, you must have to use web servics in iPhone to get coordinates.
http://iphonesdksnippets.com/post/2010/02/15/Get-Coordinates-from-Address.aspx
The short answer is no, you can't give an address and get the longitude / latitude position. Several people have written libraries that use the different web services, see this answer for details: Forward geocoding from the iPhone .
I know that you said you don't want to use various web services, but, well, you can't get everything for free. Somebody's CPU cycles are going to have to do a search. Most of them seem to me to have terms that are acceptable for most applications.
Yes, it's possible, but you have to do some work. I am currently putting this effort in one of my project. The GeoNames geographical database covers all countries and contains over eight million placenames that are available for download free of charge. I am adding their cites with population over 1,000 3.9M zip file to my project. It's contain long/lat of each city. I am going to parse each city into a custom NSObject and load them into Core Data.
In my project, instead of doing a city name search, I am going to find the closet city to a particular lat/long coordinate. Haversine formula is used to calculate the distance between two points on a sphere. Here are the formula written in Objective-C and Perl.
My current progress of parsing that data can be found here. I still have work to complete.
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:YOUR_LOCATION completionHandler:^(NSArray* placemarks, NSError* error)
{
NSLog(#"completed");
if ( error )
{
NSLog(#"error = %#", error );
}
else
{
//Here you get information about the place in placemarks array
}
}];
you can get the coordinates by calling this method .this method requires the address(name of place).
-(CLLocationCoordinate2D) addressLocation:(NSString *)addrss {
NSString *urlString = [NSString stringWithFormat:#"http://maps.google.com/maps/geo?q=%#&output=csv",
[addrss stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSString *locationString = [NSString stringWithContentsOfURL:[NSURL URLWithString:urlString]];
NSArray *listItems = [locationString componentsSeparatedByString:#","];
double latitude = 0.0;
double longitude = 0.0;
if([listItems count] >= 4 && [[listItems objectAtIndex:0] isEqualToString:#"200"]) {
latitude = [[listItems objectAtIndex:2] doubleValue];
longitude = [[listItems objectAtIndex:3] doubleValue];
}
else {
//Show error
}
CLLocationCoordinate2D location;
location.latitude = latitude;
location.longitude = longitude;
return location;
}
I am new at iphone and I have a problem.
I have this code
for (int i=0; i<2; i++) {
Datos *datos = (Datos *)[arr_datos objectAtIndex:i];
CLLocationCoordinate2D coord;
AnnotationItem *annotationItem = [[AnnotationItem alloc] init];
coord.latitude =[datos.latitud doubleValue];
coord.longitude = [datos.longitud doubleValue];
NSLog(#"coord %f",coord.longitude);
NSLog(#"coord %f",coord.latitude);
[annotationItem setCoordinate:coord];
//[annotationItem setEstacion:estacion];
[mapView_ addAnnotation:annotationItem];
[annotationItem release];
}
The problem that it doesn't done anything
But if i change the coord.latitude=40.444 and coord.longitude=-3.700;
this gives me what I want, but I don't want this, because I have an array with many latitudes and longitudes. Can anyone help me with this? when i put coord.longitude=[datos.longitude floatValue];, it doesn't work?
I'm using Xcode 3.2.2
Thanks and forgive me english.
The problem is that i had change the values, I was putting wrong values. Only I have to do is change the
coord.latitude =[datos.longitud doubleValue];
coord.longitude = [datos.latitud doubleValue];
thank everyone for your time.
It seems like Datos is an object you defined since i couldn't find it in the SDK. Given that it could be a few different things:
Either that arr_datos does not have the correct (or any) data inside of it
It could be that the Datos object is incorrectly handling data passed into it and not storing it correctly.
Place a breakpoint in Xcode and verify that arr_datos has the information you think inside of it and that the datos object is correctly storing information.
CLLocationCoordinate2D is not an object so declare it like:
CLLocationCoordinate2D coord;
BTW, you should be getting warnings about CLLocationCoordinate2D *coord - can you check your compiler logs?
[suggestion1]
NSLog(#"datos.lon %#", datos.longitud);
NSLog(#"datos.lat %#", datos.latitud);
[/suggestion1]
[suggestion2]
Note too that you can iterate through all of your datos_arr with the following:
for(Datos *datos in datos_arry) {
NSLog(.....);
}
[/suggestion2]
I've recently created a new class for my iPhone application which will hold information read from a text file containing the street address and GPS points of points of interest.
The issue though is that whenever I add code to initialize the class my application loads up and the instantly quits with no errors in the console. When I remove it, everything is fine. I simply cannot see anything wrong with the code.
Here is the constructor:
#import "GPSCoordinate.h"
#implementation GPSCoordinate
-(GPSCoordinate*) initWithData:(NSString *)rawData size:(int)size
{
self = [super init];
location = [NSMutableArray arrayWithCapacity:size];
coordinates = [NSMutableArray arrayWithCapacity:(int)size];
NSArray *tokens = [rawData componentsSeparatedByString:#"#"];
for (int i = 0; i < size - 1; i++) {
//Sub tokens
NSString *line = [tokens objectAtIndex:i];
NSArray *lineTokens = [line componentsSeparatedByString:#":"];
//Store address
[location addObject:[lineTokens objectAtIndex:0]];
//Store GPS coords
NSString *coords = [lineTokens objectAtIndex:1];
coords = [[coords stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:#""]
stringByReplacingCharactersInRange:NSMakeRange([coords length]-2, 1) withString:#""];
NSArray *coordsTokens = [coords componentsSeparatedByString:#" "];
CLLocationCoordinate2D coord;
coord.latitude = [[coordsTokens objectAtIndex:0] doubleValue];
coord.longitude =[[coordsTokens objectAtIndex:1] doubleValue];
[coordinates addObject:coords];
[line release];
[lineTokens release];
[coords release];
[coordsTokens release];
}
return self;
}
#end
Here is the call I make to it in another class:
self.gps = [[GPSCoordinate alloc] initWithData:gpsRawData size:[[gpsRawData componentsSeparatedByString:#"#"] count]];
Where am I going wrong with this?
I see a number of problems.
You're not checking the return value of [super init].
You're storing autoreleased arrays in what are presumably ivars (location and coordinates).
You're passing a separate size parameter which is calculated from the rawData outside of the call, but -initWithData: makes the exact same calculation inside the method. The size: parameter seems completely superfluous here.
You're skipping the last token entirely. You should take that for loop and make the condition simply i < size. Alternately if you're targetting iOS 4.0 or above you can turn the entire loop into
[tokens enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
NSString *line = obj;
// rest of loop body
}];
Since you don't seem to need the index inside the loop, you could also just use a for-in loop (this will work on pre-4.0 iOS devices):
for (NSString *line in tokens) {
// body of loop
}
You're not checking that your data is valid. If a line contains "foo", your program will crash when it tries to access [lineTokens objectAtIndex:1]. Similarly it'll crash if you have the string "foo:" as it tries to remove the first character of the coordinates variable. In fact anything less than 2 characters after the colon will crash. It'll also crash if there's no spaces after the colon.
And finally, all those calls to -release at the end will crash. All 4 of those objects are autoreleased objects, so by calling -release on them now you're simply guaranteeing that the app will crash when the autorelease pool is drained.
You're also storing coords (e.g. the string) in your coordinates array. Presumably you meant to store coord, though you'll need to wrap it in an NSValue in order to store it in an NSArray.
I see several issues.
1) Most fundamentally, you are releasing a lot of objects that you didn't allocate. For example:
NSString *line = [tokens objectAtIndex:i];
....
[line release];
is incorrect. Review the Cocoa Memory Management Rules.
2) Why are you doing [[gpsRawData componentsSeparatedByString:#"#"] count to pass the size to
your initWithData:size: method, when you're just going to have to repeat the -componentsSeparatedByString: call inside your method. Passing a separate "size" doesn't gain you anything, involves a redundant parse of the input, and opens up more possible bugs (what if the caller passes in a "size" that doesn't match the number of "#"s in the input - you aren't handling that error condition).
3) I also see that you are assigning latitude/longitude to CLLocationCoordinate2D coord; but not doing anything with it. Is that deliberate?
I've some memory issues with CLLocation.
CLLocation *annotation = [[CLLocation alloc] initWithLatitude:[[tempDict objectForKey:#"lat"] doubleValue] longitude:[[tempDict objectForKey:#"lon"]doubleValue]];
CLLocation *item2 = [[CLLocation alloc] initWithLatitude:[newLatString doubleValue] longitude:[newLongString doubleValue]];
cell.detailTextLabel.text = [NSString stringWithFormat:#"%.1f km",[item2 distanceFromLocation:annotation]/1000];
[annotation release];
[item2 release];
So I tried to do this, but I realised that you can't set the annotation's coordinate.
CLLocationCoordinate2D tempCoordinate = annotation.coordinate;
tempCoordinate.latitude = [[tempDict objectForKey:#"lat"] doubleValue];
tempCoordinate.longitude = [[tempDict objectForKey:#"lon"] doubleValue];
annotation.coordinate = tempCoordinate;
Is there a workaround this? I don't want to be alloc/initing a CLLocation everytime cellForRowAtIndexPath is called..
your resultant object is an NSString - just create a class which contains an NSString, as well as references/ivars of the intermediate data where necessary. then using an observer idiom, just update the cells when the string changes (design it so the string depends on the coordinates). you could probably make a class which takes a set of arguments at initialization (e.g. coordinates), creates an NSString during initialization, and then refer to the result if your data never changes. it really depends on what data you expect will mutate, and at what frequency.
I don't want to be alloc/initing a
CLLocation everytime
cellForRowAtIndexPath is called..
Why not? Do you know it's causing performance problems? You're releasing them right away, so they aren't taking up extra memory. CLLocation looks like a pretty lightweight class, and the Objective-C runtime is heavily optimized, so they probably alloc / init pretty quickly. Until you see scrolling / perf / memory issue, I would go with what works and is easy to maintain.
Premature optimization is the root of all evil - Donald Knuth