My app loads the user's past polylines and displays them on the map. Then the app starts tracking and a straight line is drawn from the last updated coordinate to the first, thereby connecting the two separate lines when they should be separate (shown here
I want to remove this straight line, so I'm thinking the easiest way would be to discard coordinates that are outside a set time frame (e.g 1 minute), so polylines on the map remain separate. I'm not sure how to do this though... I'm a beginner so any suggestions would be much appreciated!
I use this code when loading the past polyline
(IBAction)didClickLoadCoordinates:(id)sender {
// get a reference to the appDelegate so you can access the global managedObjectContext
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Route"];
NSError *error;
id results = [appDelegate.managedObjectContext executeFetchRequest:request error:&error];
if ([results count]) {
polyLine = (Route *)(results[0]);
NSArray *coordinates = polyLine.coordinates;
int ct = 0;
for (CLLocation *loc in coordinates) {
NSLog(#"location %d: %#", ct++, loc);
}
// this copies the array to your mutableArray
_locationsArray = [coordinates mutableCopy];
}
NSInteger numberOfSteps = _locationsArray.count;
//convert to coordinates array to construct the polyline
CLLocationCoordinate2D clCoordinates[numberOfSteps];
for (NSInteger index = 0; index < numberOfSteps; index++) {
CLLocation *location = [_locationsArray objectAtIndex:index];
CLLocationCoordinate2D coordinate2 = location.coordinate;
clCoordinates[index] = coordinate2;
}
MKPolyline *routeLine = [MKPolyline polylineWithCoordinates:clCoordinates count:numberOfSteps];
[_mapView addOverlay:routeLine];
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
And this code to just normally update the cllocations and make the polyline
//get the latest location
CLLocation *currentLocation = [locations lastObject];
//get latest location coordinates
CLLocationDegrees latitude = currentLocation.coordinate.latitude;
CLLocationDegrees longitude = currentLocation.coordinate.longitude;
CLLocationCoordinate2D locationCoordinates = CLLocationCoordinate2DMake(latitude, longitude);
//zoom map to show users location
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(locationCoordinates, 2000, 2000);
MKCoordinateRegion adjustedRegion = [_mapView regionThatFits:viewRegion];
[_mapView setRegion:adjustedRegion animated:YES];
//store latest location in stored track array
[_locationsArray addObject:currentLocation];
//create cllocationcoordinates to use for construction of polyline
NSInteger numberOfSteps = _locationsArray.count;
CLLocationCoordinate2D coordinates[numberOfSteps];
for (NSInteger index = 0; index < numberOfSteps; index++) {
CLLocation *location = [_locationsArray objectAtIndex:index];
CLLocationCoordinate2D coordinate2 = location.coordinate;
coordinates[index] = coordinate2;
}
MKPolyline *routeLine = [MKPolyline polylineWithCoordinates:coordinates count:numberOfSteps];
[_mapView addOverlay:routeLine];
NSLog(#"%#", _locationsArray);
Related
I‘m a beginner trying to solve a problem in which I have to display the location path of the user every ten meters after the user hits ‚start‘.
I‘ve set up the location manager. The location is being updated, the problem is that it doesn't show a visual path between the starting point and the current user location.
How do I show the visual path?
I tried to calculate the route with MKDirectionsRequest and -Response and it worked well with two given locations, which in this case I don't have though. I also tried the MKMapPoints but to be honest it isn't the point of our homework, in that it would make it unnecessarily complicated.
Here is my current state:
- (void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
CLLocation *newLocation;
newLocation = [[CLLocation alloc] initWithCoordinate:CLLocationCoordinate2DMake(newLocation.coordinate.latitude, newLocation.coordinate.longitude) altitude:kCLLocationAccuracyNearestTenMeters horizontalAccuracy:kCLLocationAccuracyNearestTenMeters verticalAccuracy:newLocation.verticalAccuracy timestamp:newLocation.timestamp];
newLocation = [locations lastObject];
if (newLocation.horizontalAccuracy < 0) {
return;
}
NSInteger count = [locations count];
if (count > 1) {
CLLocationCoordinate2D coordinates[count];
for (NSInteger i = 0; i < count; i++) {
coordinates[i] = [(CLLocation *)locations[i] coordinate];
}
MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coordinates count:count];
[self.mapView addOverlay:polyline level:MKOverlayLevelAboveRoads];
}
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(self.wegAnnotation.coordinate, 200, 200);
[mapView setRegion:region];
[manager stopUpdatingLocation];
self.myLocationManager = nil;
}
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay {
MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
renderer.strokeColor = [UIColor blueColor];
renderer.lineWidth = 5.0;
return renderer;
}
User has to create his own path on google map wherever he move step by step the route is generated.
please have look on my code snippet:
- (void)locationManager:(CLLocationManager )manager didUpdateLocations:(NSArray )locations
{
CLLocation* location1 = [locations lastObject];
if (self.endPointArray == nil)
{
self.endPointArray = [[NSMutableArray alloc]init];
}
NSString *pointString=[NSString stringWithFormat:#"%f,%f",location1.coordinate.latitude,location1.coordinate.longitude];
[self.endPointArray addObject:pointString];
NSLog(#"end point array :%#",self.endPointArray);
GMSMutablePath *path = [GMSMutablePath path];
for (int i=0; i<self.endPointArray.count; i++)
{
NSArray *latlongArray = [[self.endPointArray objectAtIndex:i]componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#","]];
[path addLatitude:[[latlongArray objectAtIndex:0] doubleValue] longitude:[[latlongArray objectAtIndex:1] doubleValue]];
}
if (self.endPointArray.count>2)
{
GMSPolyline *polyline = [GMSPolyline polylineWithPath:path];
polyline.strokeColor = [UIColor redColor];
polyline.strokeWidth = 3.0f;
polyline.map = _mapView;
}
}
I have create route with help of above code.but sometimes we get unexpected coordinates from CLLocation. If we are on road (route) it's gives perfect but if we are not on road like in home it gives wrong coordinates
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations lastObject];
NSTimeInterval age = -[location.timestamp timeIntervalSinceNow];
if (age > 120) return; // ignore old (cached) updates
if (location.horizontalAccuracy < 0) return; // ignore invalid updates
if(self.oldLocation == nil)
{
self.oldLocation = location;
return
}
CLLocationDistance distance = [location distanceFromLocation: self.oldLocation];
if (distance >= 10 && location.horizontalAccuracy <=20) //you can change 10 to 20 if you want more frequent updates
{
// add location to path
self.oldLocation = location; // save newLocation for next time
}
}
Try using this code. Its from my project and it works just fine. Use it according to your need. Hope it helps you.
I am using google map SDKs in my iOS project to track the user location and a draw a route line of the user path for his movement.I want to add two images for the starting point of the user and another for user movement. I can't add these images.Please help me to add these images. Here is my code:
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
//get the latest location
CLLocation *currentLocation = [locations lastObject];
//store latest location in stored track array;
[self.locations addObject:currentLocation];
//get latest location coordinates
CLLocationDegrees Latitude = currentLocation.coordinate.latitude;
CLLocationDegrees Longitude = currentLocation.coordinate.longitude;
CLLocationCoordinate2D locationCoordinates = CLLocationCoordinate2DMake(Latitude, Longitude);
//zoom map to show users location
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(locationCoordinates, 1000,1000);
MKCoordinateRegion adjustedRegion = [mapview regionThatFits:viewRegion];
[mapview setRegion:adjustedRegion animated:YES];
NSInteger numberOfSteps = self.locations.count;
CLLocationCoordinate2D coordinates[numberOfSteps];
for (NSInteger index = 0; index < numberOfSteps; index++) {
CLLocation *location = [self.locations objectAtIndex:index];
CLLocationCoordinate2D coordinate2 = location.coordinate;
coordinates[index] = coordinate2;
}
MKPolyline *polyLine = [MKPolyline polylineWithCoordinates:coordinates count:numberOfSteps];
[mapview addOverlay:polyLine];
}
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
MKPolylineView *polylineView = [[MKPolylineView alloc] initWithPolyline:overlay];
polylineView.strokeColor = [UIColor redColor];
polylineView.lineWidth = 10.0;
return polylineView;
}
This code draw the user path. But I want this type of output.
Someone please help me.
Add annotations to mapview, for first and last objects of your self.locations array. You will also need to remove the old annotation each time the location is updated to a new one.
Let centerY and centerX be the center point for the image
CLLocationDegrees offset = 0.01; // change size here
CLLocationDegrees southWestY = centerY - offset;
CLLocationDegrees southWestX = centerX - offset;
CLLocationDegrees northEastY = centerY + offset;
CLLocationDegrees northEastX = centerX + offset;
CLLocationCoordinate2D southWest = CLLocationCoordinate2DMake(southWestY,southWestX);
CLLocationCoordinate2D northEast = CLLocationCoordinate2DMake(northEastY,northEastX);
GMSCoordinateBounds *overlayBounds = [[GMSCoordinateBounds alloc] initWithCoordinate:southWest coordinate:northEast];
GMSGroundOverlay *overlay = [GMSGroundOverlay groundOverlayWithBounds:overlayBounds icon:[UIImage imagedNamed:#"YourImage.png"]];
overlay.bearing = 0;
overlay.map = self.mapView
With this approach (unlike with the marker) the image will zoom in and out as the user zooms in/out of the map.
I am trying to get a polyline which should be there in my track application. I have gone through various questions and websites regarding this but I can't really guess where I am going wrong. It will be great if someone can help me out? :)
My Code -->
CLLocation *currentLocation = [locations lastObject];
[linerArray addObject:currentLocation];
CLLocationDegrees lat = currentLocation.coordinate.latitude;
CLLocationDegrees lngt= currentLocation.coordinate.longitude;
CLLocationCoordinate2D lccoordinates = CLLocationCoordinate2DMake(lat, lngt);
NSInteger calculateCoordinates= linerArray.count;
CLLocationCoordinate2D coordinates[calculateCoordinates];
for (NSInteger initialStart= 0; initialStart < calculateCoordinates; initialStart ++) {
CLLocation *lct = [linerArray objectAtIndex:initialStart];
CLLocationCoordinate2D cord2 = lct.coordinate;
coordinates[initialStart] = cord2;
}
//drawing a polyline
MKPolyline *pline = [MKPolyline polylineWithCoordinates:coordinates count:calculateCoordinates];
[self.viewMap addOverlay:pline];
}
//Designing overlay polyline or setting polyine view
-(MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay{
MKPolylineRenderer *polylineRender = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
polylineRender.lineWidth = 7.0f;
polylineRender.strokeColor = [UIColor blueColor];
return polylineRender;
}
In my app I have a table view that toggles the annotations on a map view. I can go back and forth between the table view and map view through the tab bar controller. My map will reload the annotations (from the selected items that are in the table view) on view did appear. I need to know when those annotations are done loading so that I can run my method that zooms to the generated region determined by the annotation cluster.
The problem was when I ran the zoom method directly after the addAnnotations method in the view did appear, it would start the zoom process before my annotations could get the correct coordinates. Thus resulting in an incorrect zoom, and my annotations moving to the correct location.
I would also like to note that I am using forward geocoding to get my annotations coordinates.
Here is my view did appear:
[super viewDidAppear:animated];
[businessMap removeAnnotations:businessPoints];
[businessPoints removeAllObjects];
UINavigationController *navVC = (UINavigationController *) [self.tabBarController.viewControllers objectAtIndex:0];
FirstViewController *VC = [navVC.viewControllers objectAtIndex:0];
for (businessInfo *business_info in VC.selectedBusinessesArray) {
businessInfoAnnotation *businessAnnotation = [[businessInfoAnnotation alloc] init];
businessAnnotation.businessInfoClass = business_info;
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:business_info.location
completionHandler:^(NSArray* geocoded, NSError* error){
if (geocoded && geocoded.count > 0) {
CLPlacemark *placemark = [geocoded objectAtIndex:0];
CLLocation *location = placemark.location;
CLLocationCoordinate2D business_cords = location.coordinate;
businessAnnotation.coordinate = business_cords;
}
}];
businessAnnotation.title = business_info.name;
[businessPoints addObject:businessAnnotation];
}
[businessMap addAnnotations:businessPoints];
[businessMap setZoomEnabled:YES];
[self zoomToFitMapAnnotations:businessMap withArray:businessPoints];
Here is my zoom method:
-(void)zoomToFitMapAnnotations:(MKMapView*)mapViews withArray:(NSArray*)anAnnotationArray
{
if([mapViews.annotations count] == 0) return;
CLLocationCoordinate2D topLeftCoord;
topLeftCoord.latitude = -90;
topLeftCoord.longitude = 180;
NSLog(#"zooming");
CLLocationCoordinate2D bottomRightCoord;
bottomRightCoord.latitude = 90;
bottomRightCoord.longitude = -180;
for(MKPointAnnotation* annotation in anAnnotationArray)
{
topLeftCoord.longitude = fmin(topLeftCoord.longitude, annotation.coordinate.longitude);
topLeftCoord.latitude = fmax(topLeftCoord.latitude, annotation.coordinate.latitude);
bottomRightCoord.longitude = fmax(bottomRightCoord.longitude, annotation.coordinate.longitude);
bottomRightCoord.latitude = fmin(bottomRightCoord.latitude, annotation.coordinate.latitude);
}
MKCoordinateRegion region;
region.center.latitude = topLeftCoord.latitude - (topLeftCoord.latitude - bottomRightCoord.latitude) * 0.5;
region.center.longitude = topLeftCoord.longitude + (bottomRightCoord.longitude - topLeftCoord.longitude) * 0.5;
region.span.latitudeDelta = fabs(topLeftCoord.latitude - bottomRightCoord.latitude) * 1.1;
region.span.longitudeDelta = fabs(bottomRightCoord.longitude - topLeftCoord.longitude) * 1.1;
region = [mapViews regionThatFits:region];
[businessMap setRegion:region animated:YES];
}
Your help is appreciated, thanks. Sorry if this is sloppy, this is my first post.
Edit:
Here is my edited geocoder method according to nevan king's answer.
[geocoder geocodeAddressString:business_info.location
completionHandler:^(NSArray* geocoded, NSError* error){
if (geocoded && geocoded.count > 0) {
CLPlacemark *placemark = [geocoded objectAtIndex:0];
CLLocation *location = placemark.location;
CLLocationCoordinate2D business_cords = location.coordinate;
businessAnnotation.coordinate = business_cords;
businessAnnotation.title = business_info.name;
[businessPoints addObject:businessAnnotation];
[businessMap addAnnotations:businessPoints];
[self zoomToFitMapAnnotations:businessMap withArray:businessPoints];
}
}
];
Also not that I tried using the count of the annotation array to determine the last annotation's geocode completionHandler to change the region only on that specific one. But this produced an inconsistent result for my region. This is the only way it was consistently keeping all annotations within view.
MKMapViewDelegate has a mapView:didAddAnnotationViews: method which is called after a group of annotation views gets placed on the map. Note that it's not the same as annotations (some annotations might not be visible depending on the zoom).
Edit: I just noticed that you're geocoding the locations first. In that case, you'll have to add the annotations to the map in the geocode completion handler. Do it on the main thread. In your code, at the time you call addAnnotations: the completion block hasn't finished yet.
You could use a dispatch group, but I believe you'd have to manually use dispatch_group_enter and dispatch_group_leave, rather than dispatch_group_async. That way you can enter the group before each call to geocodeAddressString, and leave the group when its completion block finished. When all the completion blocks have completed, dispatch_notify would call your code for the resizing.
It seems the best way to run a method after all annotations have been geocoded and loaded on the map is to run a simple count and an if statement checking the count number with an array count.
Here is what I came up with:
int pointsArrayCount = contactArray.count;
NSLog(#"Count : %d", pointsArrayCount);
int numberOfPoints = pointsArrayCount;
NSLog(#"Count Number : %d", numberOfPoints);
i = 0;
for (contactInfo *contact_info in contactArray) {
contatcInfoAnnotation *annotation = [[contatcInfoAnnotation alloc] init];
annotation.contactInfoClass = contact_info;
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:contact_info.address
completionHandler:^(NSArray* geocoded, NSError* error){
if (geocoded && geocoded.count > 0) {
CLPlacemark *placemark = [geocoded objectAtIndex:0];
CLLocation *location = placemark.location;
CLLocationCoordinate2D contact_cords = location.coordinate;
annotation.coordinate = contact_cords;
annotation.title = contact_info.name;
[mapPoints addObject:annotation];
[mapView addAnnotations:mapPoints];
i++;
NSLog(#"Count Number of Geocoded: %d", i);
if(i == numberOfPoints) {
[self zoomToFitMapAnnotations:mapView withArray:mapPoints];
}
}
}//end completionHandler
];
However, the completion handler is only able to handle up to 40 some geocodes. So this only works well when you are loading small amounts to your map while geocoding. If you are doing more than that, then you should be storing all the coordinates somewhere and then loading them separately when the map loads.