Sort user locations iOS - ios

I've an NSArray containing users information of their places, i.e their latitude and longitudes.
I also got the current user's location, now I want to sort out the list of latitudes and longitudes from the NSArray so that the current user can see his nearby users easily.
For instance, I've the following information.
NSArray *ALLINFOLAT = [ALLINFO valueForKey:#"lat"]
// having "40.880048", "40.749315", "40.749278",
NSArray *ALLINFOLNG = [ALLINFO valueForKey:#"lng"]
// having "-77.145461", "-122.258591", "-122.320566"
NSString *currentUserLat = #"78";
NSString *currentUserLng = #"87";
How can I sort out the NSDictionary ALLINFO according to currentUserLat & currentUserLng using NSSortDescriptor.

I think you are confusing people by creating separate arrays for latitude and longitude.
If I understand correctly, you have an array ALLINFO that hold informations about users and their location, and you want to sort it based on distance to the current location.
You didn't specified what objects you have in array, so I assume they are dictionaries.
Here's how you get a sorted array:
CLLocation *currentLocation = [[CLLocation alloc] initWithLatitude: 78 longitude: 87];
NSArray *orderedInfo = [ALLINFO sortedArrayUsingComparator: ^(NSDictionary *object1, NSDictionary *object2)
{
CLLocation *location1 = [[CLLocation alloc] initWithLatitude: [object1[#"lat"] doubleValue] longitude: [object1[#"lng"] doubleValue]];
CLLocation *location2 = [[CLLocation alloc] initWithLatitude: [object2[#"lat"] doubleValue] longitude: [object2[#"lng"] doubleValue]];
CLLocationDistance dist1 = [location1 distanceFromLocation:currentLocation];
CLLocationDistance dist2 = [location2 distanceFromLocation:currentLocation];
if (dist1 < dist2) {
return (NSComparisonResult)NSOrderedAscending;
} else if ( dist1 > dist2) {
return (NSComparisonResult)NSOrderedDescending;
} else {
return (NSComparisonResult)NSOrderedSame;
}
}];

NSMutableArray *ALLINFOLAT =#[#(40.880048), #(40.749315), #(40.749278)];
NSMutableArray *ALLINFOLNG =#[#(-77.145461), #(-122.258591), #(-122.320566)];
NSString *currentUserLat = #"78";
NSString *currentUserLng = #"87";
CLLocation* currentLocation =[[CLLocation alloc]initWithLatitude:[currentUserLat doubleValue] longitude:[currentUserLng doubleValue]];
NSMutableArray* locationArray = [[NSMutableArray alloc]initWithCapacity:[ALLINFOLNG count]];
for (int i=0; i<[ALLINFOLAT count]; i++) {
CLLocationDegrees latValue = [ALLINFOLAT[i] doubleValue];
CLLocationDegrees longValue = [ALLINFOLNG[i] doubleValue];
CLLocation* location = [[CLLocation alloc]initWithLatitude:latValue longitude:longValue];
[locationArray addObject:location];
}
NSArray* sortLocationArry = [locationArray sortedArrayUsingComparator:^NSComparisonResult(CLLocation* location1, CLLocation* location2) {
CLLocationDistance distA = [location1 distanceFromLocation:currentLocation];
CLLocationDistance distB = [location2 distanceFromLocation:currentLocation];
if (distA < distB) {
return (NSComparisonResult)NSOrderedAscending;
} else if ( distA > distB) {
return (NSComparisonResult)NSOrderedDescending;
} else {
return (NSComparisonResult)NSOrderedSame;
}
}];
To get sorted latitude and longitude,
[ALLINFOLAT removeAllObjects];
[ALLINFOLNG removeAllObjects];
[sortLocationArry enumerateObjectsUsingBlock:^(CLLocation* location, NSUInteger idx, BOOL *stop) {
[ALLINFOLAT addObject:#(location.coordinate.latitude)];
[ALLINFOLNG addObject:#(location.coordinate.longitude)];
}];

Following is the code for the sort by the location long/lati.
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;
}
}];
--------EDITED--------
Following code for just pass the value of your long/lati only.
NSArray *orderedUsers = [self.shopsArray sortedArrayUsingComparator: ^(double aLong, double bLong,double aLati, double bLati)
{
CLLocation *usersA = [[CLLocation alloc] initWithLatitude: aLati longitude: bLati];
CLLocation *usersB = [[CLLocation alloc] initWithLatitude: aLong longitude: bLong];
CLLocationDistance distA = [usersA distanceFromLocation:currentLocation];
CLLocationDistance distB = [usersB distanceFromLocation:currentLocation];
if (distA < distB) {
return (NSComparisonResult)NSOrderedAscending;
} else if ( distA > distB) {
return (NSComparisonResult)NSOrderedDescending;
} else {
return (NSComparisonResult)NSOrderedSame;
}
}];
May this help to you.

Related

How to get coordinates at specific distance in a path

I am using google map ios sdk, My problem is i have to get coordinates for every 1000 meter on a poly line in map. now i am able to get number of location in given path and able to accesses them using the following code snip.
-(NSMutableArray*)getCoordinates {
pathCoordinatesArray = [[NSMutableArray alloc]init];
GMSMutablePath *path = [VTDirectionManager getPath];
NSLog(#"count %d",path.count);
for (int i=0 ; i<path.count; i++) {
if (i+1 > path.count) {
return pathCoordinatesArray;
}
for (int j = i+1; j<path.count; j++) {
CLLocationCoordinate2D sourceCoordinate = [path coordinateAtIndex:i];
CLLocation *sourceLocation = [[CLLocation alloc]initWithLatitude:sourceCoordinate.latitude longitude:sourceCoordinate.longitude];
CLLocationCoordinate2D destinationCoordinate = [path coordinateAtIndex:j];
CLLocation *destinationLocation = [[CLLocation alloc]initWithLatitude:destinationCoordinate.latitude longitude:destinationCoordinate.longitude];
BOOL check ;
check = [self checkDistanceForSource:sourceLocation andDestination:destinationLocation];
//jump to next 1000 distance position
if (check) {
i = j;
}
}
}
return pathCoordinatesArray;
}
-(BOOL)checkDistanceForSource:(CLLocation*)source andDestination:(CLLocation*)destination {
CLLocationDistance distance = [source distanceFromLocation:destination];
if (distance > 1000) {
CLLocation *location = [[CLLocation alloc] initWithLatitude:destination.coordinate.latitude longitude:destination.coordinate.longitude];
[pathCoordinatesArray addObject:location];
return YES;
}
return NO;
}
Suppose if i have 5000 meter distance path , then i have to get 5 coordinates ,each at 1000 meters position sequentially.
i think it is wrong code . Suggest me with optimized code
see the image each points are 1000 metered distance .
Please change your loop code like this
for (int i=0 ; i<path.count; i++) {
if (pathCoordinatesArray.count == 0) {
CLLocationCoordinate2D temp2d = [path coordinateAtIndex:i];
CLLocation *tempLoc = [[CLLocation alloc]initWithLatitude:temp2d.latitude longitude:temp2d.longitude];
[pathCoordinatesArray addObject:tempLoc];
}
CLLocationCoordinate2D destinationCoordinate = [path coordinateAtIndex:i];
CLLocation *destinationLocation = [[CLLocation alloc]initWithLatitude:destinationCoordinate.latitude longitude:destinationCoordinate.longitude];
[self checkDistanceForSource:[pathCoordinatesArray lastObject] andDestination:destinationLocation];
}
}

Do not add annotations repeated

I am inserting some annotations that are coming from a json server, but I wanted to check if the annotation is already on the map, if so, does not add it again. For they are being added on each other , have someone help me solve this problem?
my code:
// adiciona produtos ao mapa
- (void)adicionaAnnotationsNoMapa:(id)objetos{
NSMutableArray *annotationsPins = [[NSMutableArray alloc] init];
for (NSDictionary *annotationDeProdutos in objetos) {
CLLocationCoordinate2D location;
AnnotationMap *myAnn;
myAnn = [[AnnotationMap alloc] init];
location.latitude = [[annotationDeProdutos objectForKey:#"latitude"] floatValue];
location.longitude = [[annotationDeProdutos objectForKey:#"longitude"] floatValue];
myAnn.coordinate = location;
myAnn.title = [annotationDeProdutos objectForKey:#"name"];
myAnn.subtitle = [NSString stringWithFormat:#"R$ %#",[annotationDeProdutos objectForKey:#"price"]];
myAnn.categoria = [NSString stringWithFormat:#"%#", [annotationDeProdutos objectForKey:#"id_categoria"]];
myAnn.idProduto = [NSString stringWithFormat:#"%#", [annotationDeProdutos objectForKey:#"id"]];
[annotationsPins addObject:myAnn];
}
[self.mapView addAnnotations:annotationsPins];
}
You can iterate through annotations already on MkMapView and see if they are already there:
NSArray * annotations = [self.mapView.annotations copy];
for (NSDictionary *annotationDeProdutos in objetos)
{
// Check if annotation in annotations are duplicate of annotationDeProdutos.
// You can match using name.
}
Or the other way if you only want to show annotations from single server call:
[self.mapView removeAnnotations: self.mapView.annotations];
// Now add from JSON response.
You can do this for each of your annotations:
if(![self.mapView.annotations containsObject: myAnn]) {
[self.mapView addAnnotations: myAnn];
}
I solved the problem with this code :
// adiciona produtos ao mapa
- (void)adicionaAnnotationsNoMapa:(id)objetos{
NSMutableArray *annotationsPins = [[NSMutableArray alloc] init];
for (NSDictionary *annotationDeProdutos in objetos) {
CLLocationCoordinate2D location;
AnnotationMap *myAnn;
myAnn = [[AnnotationMap alloc] init];
location.latitude = [[annotationDeProdutos objectForKey:#"latitude"] floatValue];
location.longitude = [[annotationDeProdutos objectForKey:#"longitude"] floatValue];
myAnn.coordinate = location;
myAnn.title = [annotationDeProdutos objectForKey:#"name"];
myAnn.subtitle = [NSString stringWithFormat:#"R$ %#",[annotationDeProdutos objectForKey:#"price"]];
myAnn.categoria = [NSString stringWithFormat:#"%#", [annotationDeProdutos objectForKey:#"id_categoria"]];
myAnn.idProduto = [NSString stringWithFormat:#"%#", [annotationDeProdutos objectForKey:#"id"]];
if (self.mapView.annotations.count <1) {
[annotationsPins addObject:myAnn];
} else {
__block NSInteger foundIndex = NSNotFound;
[annotationsPins enumerateObjectsUsingBlock:^(AnnotationMap *annotation, NSUInteger idx, BOOL *stop) {
CLLocation *loc1 = [[CLLocation alloc] initWithLatitude:location.latitude longitude:location.longitude];
CLLocation *loc2 = [[CLLocation alloc] initWithLatitude:annotation.coordinate.latitude longitude:annotation.coordinate.longitude];
if ([loc1 distanceFromLocation:loc2] <= 1.0f) {
foundIndex = idx;
*stop = YES;
}
}];
if (foundIndex != NSNotFound) {
[annotationsPins addObject:myAnn];
}
}
}
[self.mapView addAnnotations:annotationsPins];
}

Only showing one pin on the map from my array

I am having trouble showing pins on a map loading coordinates, title and subtitle from parse.com.
Here is my code. Is there any logical wrong? I get only the third row in the db showing, instead of all five that I have at the parse.com.
for(int i = 0; i<objects.count; i++)
{
int raknare = 1;
raknare++;
PFObject *tempObject = [kundUppgifter objectAtIndex:raknare];
PFGeoPoint *geoPoint = [tempObject objectForKey:#"CLAT"];
PFGeoPoint *geoPoint2 = (PFGeoPoint *)geoPoint;
NSString *cname = #"CNAME";
NSString *ctown = #"CTOWN";
kundUppgifter = [[NSArray alloc] initWithArray:objects];
objects = [NSArray arrayWithObjects:cname,ctown,geoPoint,nil];
NSMutableArray * locations = [[NSMutableArray alloc]init];
Annotation * myAnn;
myAnn = [[Annotation alloc]init];
CLLocationCoordinate2D location;
double longitude;
double latitude;
latitude = geoPoint2.latitude;
longitude = geoPoint2.longitude;
location.latitude = latitude;
location.longitude = longitude;
geoPoint.latitude = latitude;
geoPoint.longitude = longitude;
myAnn.coordinate = location;
myAnn.title = [tempObject objectForKey:#"CNAME"];
myAnn.subtitle = [tempObject objectForKey:#"CTOWN"];
[locations addObject:myAnn];
[self.myMapView addAnnotations:locations];
NSLog(#"geopoint is a pfobject with latitude %f, and longitude %f kundnamn %#,stad %#,", latitude, longitude, cname, ctown);
NSLog(#"%#", cname);
NSLog(#"%#", objects);
}
}
}];
Happy if you can help!
for(int i = 0; i<objects.count; i++)
{
int raknare = 1;
raknare++;
PFObject *tempObject = [kundUppgifter objectAtIndex:raknare];
}
This code inside of your loop will make raknare == 2 every iteration of the loop. You want to use the i variable you set to pull from -objectAtIndex: like this
for(int i = 0; i<objects.count; i++)
{
// This gives you a different object each iteration of the loop
PFObject *tempObject = [kundUppgifter objectAtIndex:i];
}

Sorting Distance in UITableview based on current location

How can i sort the array based on distance from current location and show in tableview .when i use below code for sorting am not getting any proper results ,am getting the array with random distance.can any one guide me for solve this issue
-(void)sort_distance{
// Determine distance between current location and geofence location
[distance_array removeAllObjects];
NSMutableDictionary *dict=[[NSMutableDictionary alloc] init];
CLLocation *pinLocation;
CLLocation *userLocation;
for (int i=0; i<[self.geofences count]; i++) {
//dict =[self.geofences objectAtIndex:i];
CLLocationDegrees firstLat = [self.currentLatitude doubleValue];
CLLocationDegrees firstLong = [self.currentLongitude doubleValue];
CLLocationDegrees secondLat = [[[self.geofences objectAtIndex:i] objectForKey:#"lat"] doubleValue];
CLLocationDegrees secondLong = [[[self.geofences objectAtIndex:i] objectForKey:#"lon"] doubleValue];
pinLocation = [[CLLocation alloc]
initWithLatitude:secondLat
longitude:secondLong];
userLocation = [[CLLocation alloc]
initWithLatitude:firstLat
longitude:firstLong];
CLLocationDistance distance = [pinLocation distanceFromLocation:userLocation]/1000;
//current distance is NSNumber
CLLocationDistance kilometers = distance /1000;
// or you can also use this..
distanceString = [[NSString alloc] initWithFormat: #"%.1f Km", kilometers];
NSLog(#"sa: %#", distanceString);
[distance_array addObject:distanceString];
[dict setObject:[NSNumber numberWithDouble:distance] forKey:#"newdistance"];
}
NSLog(#"distance array: %#", distance_array);
// Sorting the Geofence-Location depends on distance from current location
NSSortDescriptor * sort = [[NSSortDescriptor alloc] initWithKey:#"newdistance" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sort];
sortedArray = [self.geofences sortedArrayUsingDescriptors:sortDescriptors];
}
My out put is
distance array: (
"0.2 Km",
"0.4 Km",
"0.2 Km",
"0.6 Km",
"0.7 Km",
"1.0 Km",
"0.3 Km"
)
Thanks in Advance,
Chandu.
-(void)sort_distance{
// Determine distance between current location and geofence location
[distance_array removeAllObjects];
NSMutableDictionary *dict=[[NSMutableDictionary alloc] init];
CLLocation *pinLocation;
CLLocation *userLocation;
for (int i=0; i<[self.geofences count]; i++) {
//dict =[self.geofences objectAtIndex:i];
CLLocationDegrees firstLat = [self.currentLatitude doubleValue];
CLLocationDegrees firstLong = [self.currentLongitude doubleValue];
CLLocationDegrees secondLat = [[[self.geofences objectAtIndex:i] objectForKey:#"lat"] doubleValue];
CLLocationDegrees secondLong = [[[self.geofences objectAtIndex:i] objectForKey:#"lon"] doubleValue];
pinLocation = [[CLLocation alloc]
initWithLatitude:secondLat
longitude:secondLong];
userLocation = [[CLLocation alloc]
initWithLatitude:firstLat
longitude:firstLong];
CLLocationDistance distance = [pinLocation distanceFromLocation:userLocation]/1000;
//current distance is NSNumber
CLLocationDistance kilometers = distance /1000;
// or you can also use this..
distanceString = [[NSString alloc] initWithFormat: #"%.1f km", kilometers];
NSLog(#"sa: %#", distanceString);
[distance_array addObject:distanceString];
[dict setObject:[NSNumber numberWithDouble:distance] forKey:#"newdistance"];
}
NSLog(#"distance array: %#", distance_array);
// Sorting the Geofence-Location depends on distance from current location
sortedArray = [distance_array sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"%#",sortedArray);
}
CLLocationDistance is double type, while you are saving it as integer type. So you will loose some precision. Instead use `[NSNumber numberWithDouble:].
This will sort in ascending order, for descending order return NSOrderedAscending in place of NSOrderedDescending and vice-versa.
-(CLLocationDistance )distanceOfCurrentPosition:(CLLocation *)userLocation WithPosition:(NSDictionary *)postition{
CLLocationDegrees secondLat = [postition[#"lat"] doubleValue];
CLLocationDegrees secondLong = [postition[#"lon"] doubleValue];
CLLocation *pinLocation = [[CLLocation alloc]
initWithLatitude:secondLat
longitude:secondLong];
CLLocationDistance distance = [pinLocation distanceFromLocation:userLocation]/1000;
CLLocationDistance kilometers = distance /1000;
return kilometers;
}
-(void)sort_distance{
CLLocationDegrees firstLat = [self.currentLatitude doubleValue];
CLLocationDegrees firstLong = [self.currentLongitude doubleValue];
CLLocation *userLocation = [[CLLocation alloc]
initWithLatitude:firstLat
longitude:firstLong];
[_geofences sortUsingComparator:^NSComparisonResult(NSDictionary *position1, NSDictionary *position2) {
CLLocationDistance distanceWithPosition1 = [self distanceOfCurrentPosition:userLocation WithPosition:position1];
CLLocationDistance distanceWithPosition2 = [self distanceOfCurrentPosition:userLocation WithPosition:position2];
if (distanceWithPosition1> distanceWithPosition2) {
return NSOrderedDescending;
}else if(distanceWithPosition2 > distanceWithPosition1){
return NSOrderedAscending;
}else{
return NSOrderedSame;
}
}];
NSLog(#"Order :%#",_geofences);
}
Try these for sort your array
-(NSArray *)sortArray:(NSArray *)arrayForSort forKey:(NSString *)key
{
return [arrayForSort sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
if([obj1 isKindOfClass:[NSDictionary class]] && [obj2 isKindOfClass:[NSDictionary class]])
return [[obj1 objectForKey:key] compare:[obj2 objectForKey:key]];
return NSOrderedSame;
}];
}
Just call it your case
self.geofences = [self sortArray:self.geofences forKey:#"newdistance"];
Have a nice day :)

property identifier not found on CLRegion

I am having an array of CLregions and this is my code:
NSArray *_regionArray;
NSArray *_geofences_regions;
... in the ViewDidLoad
_geofences_regions = [self buildGeofenceData];
....
- (NSArray*) buildGeofenceData {
NSString* plistPath = [[NSBundle mainBundle] pathForResource:#"regions" ofType:#"plist"];
_regionArray = [NSArray arrayWithContentsOfFile:plistPath];
NSMutableArray *geofences = [NSMutableArray array];
for(NSDictionary *regionDict in _regionArray) {
CLRegion *region = [self mapDictionaryToRegion:regionDict];
[geofences addObject:region];
}
return [NSArray arrayWithArray:geofences];
}
- (CLRegion*)mapDictionaryToRegion:(NSDictionary*)dictionary {
NSString *title = [dictionary valueForKey:#"title"];
CLLocationDegrees latitude = [[dictionary valueForKey:#"latitude"] doubleValue];
CLLocationDegrees longitude =[[dictionary valueForKey:#"longitude"] doubleValue];
CLLocationCoordinate2D centerCoordinate = CLLocationCoordinate2DMake(latitude, longitude);
CLLocationDistance regionRadius = [[dictionary valueForKey:#"radius"] doubleValue];
return [[CLRegion alloc] initCircularRegionWithCenter:centerCoordinate
radius:regionRadius
identifier:title];
}
In the following code, the property identifier is not found on object of type id.
Why is that? Shouldn't _geofences_regios[i] return a CLRegion?
/*check for region*/
CLLocationCoordinate2D coordinate = [bestLocation coordinate];
for (int i=0; i<[_geofences_regions count]; i++) {
if ([_geofences_regions[i] containsCoordinate:coordinate]) {
[self.delegate locationManager:self regionEntered:_geofences_regions[i].identifier ];
}
}
/end checking for region/
Please answer on the above question, do not suggest me of using other delegate methods like didEnterRegion.
Thanks!
That's because the object returned by the array, by default is of type id that doesn´t have an identifier attribute, you can do a cast to avoid the error:
[self.delegate locationManager:self regionEntered:((CLRegion*)_geofences_regions[i]).identifier ];

Resources