How to implement geofencing code for apple map ios 9 - ios

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.

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];
}

iOS Compass App : Location coordinates keeps changing. How to stabilitse it?

I have just completed compass app which will show distance between 2 coordinates.
Here is working code :
....
// created a timer to call locationUpdate method : 5sec
[NSTimer scheduledTimerWithTimeInterval:5 target: self selector: #selector(locationUpdate) userInfo: nil repeats: YES];
....
-(void)locationUpdate {
[locationManager startUpdatingLocation];
}
-(void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLoc
fromLocation:(CLLocation *)oldLoc
{
//To get the current location Latitude & Longitude.
float latitude = locationManager.location.coordinate.latitude;
float longitude = locationManager.location.coordinate.longitude;
startPoint = [[CLLocation alloc] initWithLatitude: latitude longitude: longitude ]; //Current Latitude and Longitude
endPoint = [[CLLocation alloc] initWithLatitude: 12.923670 longitude: 77.573496]; //Target Latitude and Longitude -----------------------------------> Need to come from database.
//To get the distance from the 2 coordinates in feet
CLLocationDistance distInMeter = [startPoint distanceFromLocation:endPoint];
//Lable veiw to update remaining distance.
if(distInMeter > 999.000f) {
self.labelLongLat.text = [NSString stringWithFormat: #"Remainig distance %.2f KM with Lat : %lf LAN %lf", distInMeter / 1000, latitude, longitude ];
}else
self.labelLongLat.text = [NSString stringWithFormat: #"Remainig distance %.2f M Lat : %lf LAN %lf" , distInMeter, latitude,longitude ];
}
My problem is while updating location for each 5sec, the coordinates varies a lot. That will result in Remaining distance calculation. Which is highly unstable!! How can I fix this?
Thanks in Advance,
The delegate method you are using is deprecated. You should use locationManager:didUpdateLocations:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *currentLocation = [locations lastObject];
endPoint = [[CLLocation alloc] initWithLatitude: 12.923670 longitude: 77.573496]; //Target Latitude and Longitude -----------------------------------> Need to come from database.
//To get the distance from the 2 coordinates in meters
CLLocationDistance distInMeter = [currentLocation distanceFromLocation:endPoint];
//Label view to update remaining distance.
if(distInMeter > 999 ) {
self.labelLongLat.text = [NSString stringWithFormat: #"Remaining distance %.2f Km with Lat : %lf LAN %lf", distInMeter / 1000, currentLocation.coordinate.latitude, currentLocation.coordinate.longitude ];
} else {
self.labelLongLat.text = [NSString stringWithFormat: #"Remaining distance %.2f m Lat : %lf LAN %lf" , distInMeter, currentLocation.coordinate.latitude, currentLocation.coordinate.longitude ];
}
}
The accuracy of your location can be affected by signal quality (tall buildings, indoor location etc). You can examine the horizontalAccuracy property of your location to see how accurate the position is. If the accuracy is low then you can defer updating your label. Beware that you may never get an accurate fix.
One strategy is to wait for an accuracy <20m or after 5 updates -
#property (nonatomic) NSInteger locationUpdates;
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
self.locationUpdates++;
CLLocation *currentLocation = [locations lastObject];
if (currentLocation.horizontalAccuracy < 20.0 || self.locationUpdates>5) {
endPoint = [[CLLocation alloc] initWithLatitude: 12.923670 longitude: 77.573496]; //Target Latitude and Longitude -----------------------------------> Need to come from database.
//To get the distance from the 2 coordinates in meters
CLLocationDistance distInMeter = [currentLocation distanceFromLocation:endPoint];
//Label view to update remaining distance.
if(distInMeter > 999 ) {
self.labelLongLat.text = [NSString stringWithFormat: #"Remaining distance %.2f Km with Lat : %lf LAN %lf", distInMeter / 1000, currentLocation.coordinate.latitude, currentLocation.coordinate.longitude ];
} else {
self.labelLongLat.text = [NSString stringWithFormat: #"Remaining distance %.2f m Lat : %lf LAN %lf" , distInMeter, currentLocation.coordinate.latitude, currentLocation.coordinate.longitude ];
}
}
}

Trouble while getting distance between two points

In my iOS app I've to track the distance from a a start point to my current location. I implemented this code:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
CLLocation *currentLocation = [locations lastObject];
CLLocation *startLocation = [locations firstObject];
[coordArray addObject:currentLocation];
float speed = currentLocation.speed * 3.6;
if (speed > 0) {
self.labelSpeed.text = [NSString stringWithFormat:#"%.2f Km/h", speed];
[speedArray addObject:[NSNumber numberWithFloat:speed]];
}
CLLocationDistance distance = [startLocation distanceFromLocation:currentLocation];
}
But when I try to use the app it doesn't get a distance. I need the distance to show it in a label and with distance I will calculate the number of steps by using this equation:
steps = distance / length of human step
I know that it's not accurate, but I can't use accelerometer, because it doesn't work while the display of the iPhone is not active. A person suggested me this solution. Why my code doesn't give me a distance?
The callback
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
does give you at least one location, and locations array hold sonly multiple objects if the location update was deferred before. To get the walking/driving distance, you have to store the initial location in a class variable or property. Then, when you want to calculate a distance, do as in your code above, but with the class variable that holds the initial location.
Check below code for getting distance:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
if ([coordArray count]>0) {
CLLocation *currentLocation = manager.location;
CLLocation *startLocation = [coordArray objectAtIndex:0];
[coordArray addObject:currentLocation];
float speed = currentLocation.speed * 3.6;
if (speed > 0) {
NSLog(#"\n speed:%#",[NSString stringWithFormat:#"%.2f Km/h", speed]);
}
CLLocationDistance distance = [startLocation distanceFromLocation:currentLocation];
if (distance>0) {
NSLog(#"Distance:%#",[NSString stringWithFormat:#"%lf meters",distance]);
}
}
else{
[coordArray addObject:manager.location];
}
}

ios: Debug custom location not working

In simulator I set debug->location->custom location latitude and longitude.
Then in code I have:
- (void)updateMap{
MKCoordinateSpan span;
span.latitudeDelta = .6;//The amount of north-to-south distance (measured in degrees) to display on the map
span.longitudeDelta = .6;//The amount of east-to-west distance (measured in degrees) to display for the map region
MKCoordinateRegion region;
region.center = _ls.coordinate;
region.span = span;
[mapView setRegion:region animated:TRUE];
}
In a different file I obtain #property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
//Get the latitude and longitude location
CLLocation *lastLocation = [locations lastObject];
_latitude = [[NSString alloc] initWithFormat:#"%f", lastLocation.coordinate.latitude];
_longitude = [[NSString alloc] initWithFormat:#"%f", lastLocation.coordinate.longitude];
_coordinate = lastLocation.coordinate;//manager.location.coordinate;
}
When I run the app the simulator keeps showing San Francisco. Any ideas why?
It is the default location for the simulator. To simulate the location use the drop down in the Xcode window to set a location or a "Path" to simulate a movement.
Got it working:
(1)I just had to shut down xcode and simulator and reload the program. Now it loads my location.:)

Resources