find locations (from mutablearray) close to users location ios - ios

)
i have an mutable array of restaurantObjects serialized form json data, if current object is named restaurantDetail i can acces corrdinates like this
float x = [[restaurantDetail.coordinates objectAtIndex:0] floatValue];
float y = [[restaurantDetail.coordinates objectAtIndex:1] floatValue];
so i would like to extract restaurants which are close to users location and store them to array
and if user changes location array should get updated too
i would really apprichiate help because i am completely lost here
i have tried this form Muthub Ram
- (void) checkIfInRegion{
Restaurant * currentRestaurant;
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
for(int i=0;i<[restaurantsArray count];i++){
currentRestaurant = [restaurantsArray objectAtIndex:i];
//NSLog(#"%#",currentRestaurant.coordinates);
CLLocation * currentRestLoc = [[CLLocation alloc] initWithCoordinate:CLLocationCoordinate2DMake([[currentRestaurant.coordinates objectAtIndex:0] floatValue], [[currentRestaurant.coordinates objectAtIndex:1] floatValue])
altitude:0
horizontalAccuracy:0
verticalAccuracy:0
timestamp:[NSDate date]];
NSLog(#"%#",currentRestLoc);
if([locationManager.location distanceFromLocation:currentRestLoc] <100)
{
//if less than 100meters
//then add object in this array
[near_by_user addObject:[restaurantsArray objectAtIndex:i]];
}
}
NSLog(#"%#",locationManager.location);
NSLog(#"restaurants near%#",near_by_user);
[locationManager stopUpdatingLocation];
}
i get all coordinates form location but i dont get any objects in near_by_user array ( it remains empty)
http://pastebin.com/fc8q69Le here is log, last set of coordinates is my location same as one of restaurants

for(i=0;i<[array_res count];i++){
if([clloc distanceFromLocation:[array_res objectatindex:i]<100])
{
//if less than 100meters
//then add object in this array
[near_by_user addobject:[array_res objectatindex:i];
}
}
please add this code.if you have any clarification update here

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 :)

How to Add the geofence of a region to monitoring.

- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
// If it's a relatively recent event, turn off updates to save power
NSLog(#"%# locations",locations);
float Lat = _locationManager.location.coordinate.latitude;
float Long = _locationManager.location.coordinate.longitude;
NSLog(#"Lat : %f Long : %f",Lat,Long);
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(28.52171,77.2015457);
NSLog(#"center check %#",center);
CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center
radius:500
identifier:#"new region"];
BOOL doesItContainMyPoint = [region containsCoordinate:CLLocationCoordinate2DMake(Lat,Long)];
NSLog(#"success %hhd", doesItContainMyPoint);
}
the issue is ,here i m providing a static region for which i m checking (center)
but the requirement is, this region will take the lat n long of the riders and riders can vary in number
i hv all lat n long in an array of dictionary. First the driver will pick the first rider in the list and at that time i need the region of rider 1 location.
I m not getting any idea how to achieve this
if i do like this
for (NsMutableDictionary * dict in goersList)
{
rider_id=[dict valueForKey:#"trip_id"];
lat=[dict valueForKey:#"origin_lat"];
longi=[dict valueForKey:#"origin_long"];
}
then how will it know that the first region is to be monitored and after existing from that range i hv to check for second location
You can create dynamically regions and add them to monitoring.
for (NSDictionary *dict in [result valueForKey:#"Geofences"])
{
NSLog(#"%#",dict);
CLLocationCoordinate2D locationCoordinate=CLLocationCoordinate2DMake([[dict valueForKey:#"cLatitude"]doubleValue], [[dict valueForKey:#"cLongitude"]doubleValue]);
CLCircularRegion *circularRegion=[[CLCircularRegion alloc]initWithCenter:locationCoordinate radius:[[dict valueForKey:#"Radius"]doubleValue] identifier:[dict valueForKey:#"Name"]];
circularRegion.notifyOnEntry=YES;
circularRegion.notifyOnExit=YES;
[[AppDelegate sharedDelegate].locationManager startMonitoringForRegion:circularRegion];
NSLog(#"%#",[[[AppDelegate sharedDelegate] locationManager].monitoredRegions description]);
}
Here there are several regions are added to monitoring. You can add single at a time. i.e on selection of tableview.
And remove others using below code
for (CLRegion *monitored in [[AppDelegate sharedDelegate].locationManager monitoredRegions])
{
[[AppDelegate sharedDelegate].locationManager stopMonitoringForRegion:monitored];
}

How to implement geofencing code for apple map ios 9

I have two locations one is the driver and other is the rider.I have lat n long available for both.I want to hit an api when the driver enters in the geofence area of the riders location.
i went through QKGeofenceManager demo project:
using this i can provide lat n long and the radius to find geofence.
But the issue is do i have to update driver location every time in background and what condition should be applied so the the callback is made when the driver enters the geofence area of rider.If the ap is in background how will it handle everything.
Do i have to make any changes in appdelegate
- (NSArray *)geofencesForGeofenceManager:(QKGeofenceManager *)geofenceManager
{
NSArray *fetchedObjects = [self.fetchedResultsController fetchedObjects];
NSMutableArray *geofences = [NSMutableArray arrayWithCapacity:[fetchedObjects count]];
for (NSManagedObject *object in fetchedObjects) {
NSString *identifier = [object valueForKey:#"identifier"];
CLLocationDegrees lat = [[object valueForKey:#"lat"] doubleValue];
CLLocationDegrees lon = [[object valueForKey:#"lon"] doubleValue];
CLLocationDistance radius = [[object valueForKey:#"radius"] doubleValue];
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(lat, lon);
CLCircularRegion *geofence = [[CLCircularRegion alloc] initWithCenter:center radius:radius identifier:identifier];
[geofences addObject:geofence];
}
return geofences;
}
I found an alternative to achieve this task.
As my delegate method of Didenterlocation was not called,i applied another approach.
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
// If it's a relatively recent event, turn off updates to save power
NSLog(#"%# locations",locations);
float Lat = _locationManager.location.coordinate.latitude;
float Long = _locationManager.location.coordinate.longitude;
NSLog(#"Lat : %f Long : %f",Lat,Long);
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(28.58171,77.2915457);
NSLog(#"center check %#",center);
CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center
radius:500
identifier:#"new region"];
BOOL doesItContainMyPoint = [region containsCoordinate:CLLocationCoordinate2DMake(Lat,Long)];
NSLog(#"success %hhd", doesItContainMyPoint);
}
by keeping track of the current location,whenever the current location coordinates enter into the coordinates of the center region,you can fire a notification that the user has entered this particular region.

loop array of locations in separate thread

I'm working on an application for the iPhone and I'm keeping track of the users current location. When the didupdateLocations delegate method actually executes i would like to test if the location in the NSArray is in a predefined array the contains other locations, this array mind you could grow over time.
I'm running a for loop within this method to test against my own array of locations but i would like to move that to a separate thread. So in case my own array with multiple locations grows to a large number the for loop does not freeze my UI.
I have tried it like this but I'm getting undesirable results. I understand that the location tracking definitely happens in a separate thread. However those didupdateLocations execute on a separate thread. The Apple doc's are not very clear on the matter. My end goal again is to compare against my array and not lock the UI.
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)thisLocation {
dispatch_queue_t queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// get the last object in the array of locations
CLLocation* location = [thisLocation lastObject];
dispatch_async(queue, ^{
[self checkmyArray:location];
});
}
-(void)checkmyArray:(CLLocation *)workingLocation{
NSLog(#"SoundTheAlarm");
int alarm_on_c = 0;
NSUInteger tmp_count = [theData.LocationsObjectArray count];
BOOL alarm;
NSMutableDictionary * tempObject;
CLLocationDistance distance = 0.0;
for (int i = 0; i < tmp_count; i++) {
tempObject= [theData.LocationsObjectArray objectAtIndex:i];
thisLoc = [[tempObject objectForKey:#"onoff"] isEqual:#YES];
if (thisLoc) {
//check if we are near that location
double lat = [[tempObject objectForKey:#"latitude"] doubleValue];
double lon = [[tempObject objectForKey:#"longitude"] doubleValue];
// goal = [[CLLocation alloc] initWithLatitude:40.097771 longitude:-74.941399];
goal = [[CLLocation alloc] initWithLatitude:lat longitude:lon];
// check the destination between current location and goal location - in meters
distance = [goal distanceFromLocation:workingLocation];
NSLog(#"distance %f\n", distance);
}
// if distance from goal is less than 350 meters
if (distance <= 350){
[self scheduleNotification:[tempObject objectForKey:#"name"]];
// turn off tracking for this location
[tempObject setObject:#NO forKey:#"onoff"];
[theData.LocationsObjectArray replaceObjectAtIndex:i withObject:tempObject];
NSIndexPath *path = [NSIndexPath indexPathForRow:i inSection:0];
ExtendedSavedCellTableViewCell *cell = (ExtendedSavedCellTableViewCell *)[self.tableView cellForRowAtIndexPath:path];
cell.switchView.on = NO;
// save the update to the switch to the database as well
NSString *lat = [tempObject objectForKey:#"latitude"];
/*check to determine if the uiswitch is turned off or on.*/
[self fetchedResultsController:#NO lat:lat index:path];
[self displayAlertViewForAlarm:[tempObject objectForKey:#"name"]];
}
-(void)displayAlertViewForAlarm:(NSString *)nameOfLocation{
dispatch_async(dispatch_get_main_queue(), ^(void) {
UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:#"Destination reached"
message:nameOfLocation
delegate:self
cancelButtonTitle:#"Go Away"
otherButtonTitles:#"Notify", nil];
[myAlert show];
});
}
It's generally a bad idea to use threads in iOS if you can at all avoid it. In your case, I'd implement the function which does the looping to automatically pop out of the loop after too many iterations and then schedule the next chunk of iterating to happen in another pass through the event loop. In other words, something like this:
- (void) checkLocationsStartingAt:(NSNumber)start
{
NSInteger s = (start) ? [start intValue] : 0;
for (i = s; i < list.length; i++) {
if (i > TOO_MANY) {
[self performSelector:#selector(checkLocationsStartingAt:)
withObject:#(i)
afterDelay:0.001];
return;
} else {
// check element at i
}
}
}
see: NSObject Reference
You are checking one location against an array of other locations.
You display an alert for time you are closer than 350m from a location.
You could be within 350m of many items in the list.
You have no code prevent multiple alerts.
So you will sometimes have many alerts.
You have a couple options. One will probably suit your needs better than the others. Possibly one I didn't list.
You could re-use a single UIAlertView instance and check the
visible property on it. If it's already visible, do nothing.
You could break out of the loop once you get a single "hit" on being
<350m.
You could stick in a BOOL which records if you've shown the alert and
check the BOOL every time before you show the alert again.

I need to get the penultimate result from a for loop

I have an array of waypoints stored which have latitude and longitude values. I have created a for loop to loop through the array and compare my current CLLocation positions latitude and longitude to find which of the waypoints i am closest to. I also need to get the second closest waypoint and store this ac s a CLLocation object as well but cannot get it working.
The logic would be something like this
am I closest
yes
move closest location to second closest
set as second closest
loop again to get the closest point
My code:
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
//set latestlocation as the last object from the locations array
CLLocation *currentLocation = [locations lastObject];
//declare a closestpoint object
CLLocation *closestWayPointToCurrentLocation;
//declare a second closest point object
CLLocation *secondClosestWayPointToCurrentLocation;
//set the distance to a high number
float distance = 10000000;
float secondClosestWaypointDistance = 10000000;
//load in plist
NSString *plistName = [self.mapsInfo objectForKey:#"plistName"];
NSString *path = [[NSBundle mainBundle] pathForResource:#"Chester" ofType:#"plist"];
NSString *path = [[NSBundle mainBundle] pathForResource:plistName ofType:#"plist"];
//store in array called waypoints
NSArray *waypoints= [NSArray arrayWithContentsOfFile:path];
//declare a variable for locationNum (the waypoints)
int locationNum = 0;
for (NSDictionary *point in waypoints) {
CLLocation *waypointLocation = [[CLLocation alloc]initWithLatitude:[(NSNumber *)[point objectForKey:#"Lat"]floatValue] longitude:[(NSNumber *)[point objectForKey:#"Long"]floatValue]];
float waypointDistanceFromCurrentLocation = [currentLocation distanceFromLocation:waypointLocation];
//secondClosestWayPointToCurrentLocation = waypointLocation;
if(waypointDistanceFromCurrentLocation < distance) {
//todo: move the current closestWayPointToCurrentLocation into second postion
//update the second closest waypoint distance variable also with distance
distance = waypointDistanceFromCurrentLocation;
closestWayPointToCurrentLocation = waypointLocation;
if(closestWayPointToCurrentLocation == waypointLocation) {
}
}
else
{
//check against the second position
//if closer than second position, replace it with new waypoint with code similar to above
}
If you are sure the point you is always the penultimate then you can retrive it like this
int totalNumberOfWaypoints = [waypoints count];
//Get penultimate waypoints
NSDictionary *penultimateWaypoint = [waypoints objectAtIndex:(totalNumberOfWaypoints - 2)];
Why dont you just make a sorted array of points, sorted by distance in ascending order?
NSMutableArray *waypoints= [NSArray arrayWithContentsOfFile:path];
for (NSDictionary *point in waypoints) {
[waypoints sortUsingComparator: (NSComparisonResult) ^ (id obj1, id obj2) {
CLLocation *waypointLocation1 = [[CLLocation alloc]initWithLatitude:[(NSNumber *)[obj1 objectForKey:#"Lat"]floatValue] longitude:[(NSNumber *)[obj1 objectForKey:#"Long"]floatValue]];
CLLocation *waypointLocation2 = [[CLLocation alloc]initWithLatitude:[(NSNumber *)[obj2 objectForKey:#"Lat"]floatValue] longitude:[(NSNumber *)[obj2 objectForKey:#"Long"]floatValue]];
float distance1 = [currentLocation distanceFromLocation:waypointLocation1];
float distance2 = [currentLocation distanceFromLocation:waypointLocation2];
if (distance1 < distance2) {
return NSOrderedAscending;
}
else if (distance1 > distance2) {
return NSOrderedDescending
}
else
return NSOrderedSame;
}];
This way you can always have the closest point at [waypoints objectAtIndex:0] and second closest point at [waypoints objectAtIndex:1] and so on.
less work, better result IMHO

Resources