In my app I have a latitude-longitude of 1 fix location. Now user with iPhone device can move anywhere and even he rotate his device, the arrow (some uiimageview) should point to that fix location so User will get direction to that location every time. I tried as follows.
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
CLLocationCoordinate2D here = newLocation.coordinate;
[self calculateUserAngle:here];
}
-(void) calculateUserAngle:(CLLocationCoordinate2D)user {
NSLog(#"my destination is %f ; %f", fixlocLat, fixlocLon);
degrees = degrees + atan2(sin(fixlocLon-user.longitude)*cos(fixlocLat),cos(user.latitude)*sin(fixlocLat) - sin(user.latitude)*cos(fixlocLat)*cos(fixlocLon-user.longitude));
//get angle between user location and fix location
}
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
arrow.transform = CGAffineTransformMakeRotation((degrees-newHeading.trueHeading) * M_PI / 180);
}
but the code above will always rotate arrow to approx. 2-3 degree north for every location.
My question looks similar to this Stack Overflow question.but using code from this link I am getting wrong direction.
please help me on this.
and also if any idea using accelerometer or gyrodata will be helpful for me.
thanx in advance.
This works for me: to rotate a Direction Arrow to particular location
-(void)viewDidLoad
{
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLDistanceFilterNone;
}
-(void) calculateUserAngle:(CLLocationCoordinate2D)current {
double x = 0, y = 0 , deg = 0,delLon = 0;
delLon = fixLon - current.longitude;
y = sin(delLon) * cos(fixLat);
x = cos(current.latitude) * sin(fixLat) - sin(current.latitude) * cos(fixLat) * cos(delLon);
deg = RADIANS_TO_DEGREES(atan2(y, x));
if(deg<0){
deg = -deg;
} else {
deg = 360 - deg;
}
degrees = deg;
}
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
CLLocationCoordinate2D here = newLocation.coordinate;
[self calculateUserAngle:here];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
arrow.transform = CGAffineTransformMakeRotation((degrees-newHeading.trueHeading) * M_PI / 180);
}
this code may help you
-(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading {
// Use the true heading if it is valid.
CLLocationDirection direction = newHeading.magneticHeading;
CGFloat radians = -direction / 180.0 * M_PI;
self.strAccuracy = [NSString stringWithFormat:#"%.1fmi",newHeading.headingAccuracy];
[lblAccuracy setText:self.strAccuracy];
//Rotate Bearing View
[self rotateBearingView:bearingView radians:radians];
//For Rotate Niddle
CGFloat angle = RadiansToDegrees(radians);
[self setLatLonForDistanceAndAngle];
[self rotateArrowView:arrowView degrees:(angle + fltAngle)];
}
-(void)rotateArrowView:(UIView *)view degrees:(CGFloat)degrees
{
CGAffineTransform transform = CGAffineTransformMakeRotation(DegreesToRadians(degrees));
view.transform = transform;
}
-(void)setLatLonForDistanceAndAngle
{
dblLat1 = DegreesToRadians(appDelegate.dblLatitude);
dblLon1 = DegreesToRadians(appDelegate.dblLongitude);
dblLat2 = DegreesToRadians(objClsProductSearch.dblLatitude);
dblLon2 = DegreesToRadians(objClsProductSearch.dblLongitude);
fltLat = dblLat2 - dblLat1;
fltLon = dblLon2 - dblLon1;
}
-(float)getAngleFromLatLon
{
//Calculate angle between two points taken from http://www.movable-type.co.uk/scripts /latlong.html
double y = sin(fltLon) * cos(dblLat2);
double x = cos(dblLat1) * sin(dblLat2) - sin(dblLat1) * cos(dblLat2) * cos(dblLon2);
CGFloat angle = RadiansToDegrees(atan2(y, x));
return angle;
}
-(void)viewDidLoad
{
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLDistanceFilterNone;
[locationManager startUpdatingLocation];
[locationManager startUpdatingHeading];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
GeoAngle = [self setLatLonForDistanceAndAngle:newLocation];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location=[locations lastObject];
GeoAngle = [self setLatLonForDistanceAndAngle:location];
}
-(float)setLatLonForDistanceAndAngle:(CLLocation *)userlocation
{
float lat1 = DegreesToRadians(userlocation.coordinate.latitude);
float lon1 = DegreesToRadians(userlocation.coordinate.longitude);
float lat2 = DegreesToRadians(Destination lattitude);
float lon2 = DegreesToRadians(Destination Longitude);
float dLon = lon2 - lon1;
float y = sin(dLon) * cos(lat2);
float x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon);
float radiansBearing = atan2(y, x);
if(radiansBearing < 0.0)
{
radiansBearing += 2*M_PI;
}
return radiansBearing;
}
-(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
float direction = -newHeading.trueHeading;
arrow.transform=CGAffineTransformMakeRotation((direction* M_PI / 180)+ GeoAngle);
}
Try this example. Its in C# Xamarin tho :)
"double heading" is the CLHeading.MagneticHeading from the eventobject in CLLocationManager.UpdatedHeading.
void UpdateCompass(Location origin, Location target, double heading)
{
var angle1 = GetAngleBetweenPoints(origin, target);
var angle2 = GetAngleFromHeading(heading);
var radian = Math.PI * (angle1 + angle2) / 180.0;
CompassArrow.Transform = CGAffineTransform.MakeRotation((nfloat)radian);
}
double GetAngleBetweenPoints(Location origin, Location target)
{
var n = 270 - (Math.Atan2(origin.Latitude - target.Latitude, origin.Longitude - target.Longitude)) * 180 / Math.PI;
return n % 360;
}
double GetAngleFromHeading(double heading)
{
var radians = -heading / 180.0 * Math.PI;
return radians * (180.0 / Math.PI);
}
Related
I want to get the user's current location from my iPhone app. I have tried Google search also, but can't get the exact answer. I am trying to develop the code.The location will not display properly.i can findout nearest location.But i canot find out exact location.Please give me any idea,how to display exact location.Thanks in advance.
MapviewAppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if([CLLocationManager locationServicesEnabled])
{
self.locationManager.delegate=self;
self.locationManager.distanceFilter=1;
[self.locationManager startUpdatingLocation];
}
}
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
double miles=12.0;
double scalingFactor= ABS( cos(2 * M_PI * newLocation.coordinate.latitude /360.0) );
MKCoordinateSpan span;
span.latitudeDelta=miles/69.0;
span.longitudeDelta=miles/(scalingFactor*69.0);
MKCoordinateRegion region;
region.span=span;
region.center=newLocation.coordinate;
[self.viewController.mapView setRegion:region animated:YES];
self.viewController.mapView.showsUserLocation=YES;
}
mapviewcontroller.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.mapView.showsUserLocation = YES;
MKCoordinateRegion region;
region.center = self.mapView.userLocation.coordinate;
MKCoordinateSpan span;
span.latitudeDelta = 20;
span.longitudeDelta = 20;
region.span = span;
[self.mapView setRegion:region animated:YES];
}
// create a region that fill fit all the locations in it
+ (MKCoordinateRegion) getRegionThatFitsLocations:(NSArray *)locations {
// initialize to minimums, maximums
CLLocationDegrees minLatitude = 90;
CLLocationDegrees maxLatitude = -90;
CLLocationDegrees minLongitude = 180;
CLLocationDegrees maxLongitude = -180;
// establish the min and max latitude and longitude
// of all the locations in the array
for (CLLocation *location in locations)
{
if (location.coordinate.latitude < minLatitude)
{
minLatitude = location.coordinate.latitude;
}
if (location.coordinate.latitude > maxLatitude)
{
maxLatitude = location.coordinate.latitude;
}
if (location.coordinate.longitude < minLongitude)
{
minLongitude = location.coordinate.longitude;
}
if (location.coordinate.longitude > maxLongitude)
{
maxLongitude = location.coordinate.longitude;
}
}
MKCoordinateSpan span;
CLLocationCoordinate2D center;
if ([locations count] > 1)
{
// for more than one location, the span is the diff between
// min and max latitude and longitude
span = MKCoordinateSpanMake(maxLatitude - minLatitude, maxLongitude - minLongitude);
// and the center is the min + the span (width) / 2
center.latitude = minLatitude + span.latitudeDelta / 2;
center.longitude = minLongitude + span.longitudeDelta / 2;
}
else
{
// for a single location make a fixed size span (pretty close in zoom)
span = MKCoordinateSpanMake(0.01, 0.01);
// and the center equal to the coords of the single point
// which will be the coords of the min (or max) coords
center.latitude = minLatitude;
center.longitude = minLongitude;
}
// create a region from the center and span
return MKCoordinateRegionMake(center, span);
}
Why not just enable userLocationVisible on the MKMapView? Then the users current location will be visible on the map as a blue dot.
Basically whenever you add mapView you should enable the showuserlocation option in the inspector.
The app will then start with your location when you run the app on the device.
I have a collection of CGPoints obtained from converting geographic coordinates obtained form Core Location frame work delegate method. The collection is a set of start/end points.
I am obtaining the coordinates from CoreLocation delegate method
CLLocationCoordinate2D coordinate;
CLLocationDegrees latitude;
CLLocationDegrees longitude;
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
CLLocation *location = [locations lastObject];
latitude = location.coordinate.latitude;
longitude = location.coordinate.longitude;
}
I am converting the current latitude and longitude to CGPoint as below
CGPoint startPoint = [mapView convertCoordinate:retrievedCoordinate toPointToView:self.view];
CGPoint endPoint = [mapView convertCoordinate:retrievedEndCoordinate toPointToView:self.view];
I need to draw lines on my UIView based on the values in the collection. How can I adjust/scale correctly, the CGPoints with respect to the UIView. UIView frame size is (0, 0, 768, 1004).
This is the scaling I have done
- (CGPoint)convertLatLongCoord:(CGPoint)latLong {
CGSize screenSize = [UIScreen mainScreen].applicationFrame.size;
CGFloat SCALE = MIN(screenSize.width, screenSize.height) / (2.0 * EARTH_RADIUS);
CGFloat OFFSET = MIN(screenSize.width, screenSize.height) / 2.0;
CGFloat x = EARTH_RADIUS * cos(latLong.x) * cos(latLong.y) * SCALE + OFFSET;
CGFloat y = EARTH_RADIUS * cos(latLong.x) * sin(latLong.y) * SCALE + OFFSET;
return CGPointMake(x, y);
}
where #define EARTH_RADIUS 6371
The line drawing is not correct, since somewhere the conversion to CGPoint might be wrong. What am I doing wrong. Help needed.
For convert CLLocationCoordinate2D to CGPoint see answer here - https://stackoverflow.com/a/37276779/2697425
I am using mapkit to display my map, I have also created an overlay. Now I am tracking the user using CLLocationManager and outputting that to the consol for testing. I hope someone knows or has a tutorial/example of how to place a dot or annotation where the users location is based on the lat lon results.. This is what I am doing at the moment..
- (void)viewDidLoad
{
// Initialize the TileOverlay with tiles in the application's bundle's resource directory.
// Any valid tiled image directory structure in there will do.
NSString *tileDirectory = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"Tiles"];
TileOverlay *overlay = [[TileOverlay alloc] initWithTileDirectory:tileDirectory];
[map addOverlay:overlay];
// zoom in by a factor of two from the rect that contains the bounds
// because MapKit always backs up to get to an integral zoom level so
// we need to go in one so that we don't end up backed out beyond the
// range of the TileOverlay.
MKMapRect visibleRect = [map mapRectThatFits:overlay.boundingMapRect];
visibleRect.size.width /= 2;
visibleRect.size.height /= 2;
visibleRect.origin.x += visibleRect.size.width / 2;
visibleRect.origin.y += visibleRect.size.height / 2;
map.visibleMapRect = visibleRect;
// map.showsUserLocation = YES;
//location tracking
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
[overlay release]; // map is now keeping track of overlay
}
//OverLays Topographical map
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
TileOverlayView *view = [[TileOverlayView alloc] initWithOverlay:overlay];
view.tileAlpha = 0.6;
return [view autorelease];
}
//Tracks Users location and Prints out the Lat and Lon
-(void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
CLLocationCoordinate2D here = newLocation.coordinate;
NSLog(#"%f %f ", here.latitude, here.longitude);
}
//Call [locationManager stopUpdatingLocation] as some point
I am just wondering how to center the map view as the user is vein tracked with CLLocationmanager and map kit
This is currently how I am tracking the user and updating the location etc.
- (void)viewDidLoad
{
// Initialize the TileOverlay with tiles in the application's bundle's resource directory.
// Any valid tiled image directory structure in there will do.
NSString *tileDirectory = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"Tiles"];
TileOverlay *overlay = [[TileOverlay alloc] initWithTileDirectory:tileDirectory];
[map addOverlay:overlay];
// zoom in by a factor of two from the rect that contains the bounds
// because MapKit always backs up to get to an integral zoom level so
// we need to go in one so that we don't end up backed out beyond the
// range of the TileOverlay.
MKMapRect visibleRect = [map mapRectThatFits:overlay.boundingMapRect];
visibleRect.size.width /= 2;
visibleRect.size.height /= 2;
visibleRect.origin.x += visibleRect.size.width / 2;
visibleRect.origin.y += visibleRect.size.height / 2;
map.visibleMapRect = visibleRect;
// map.showsUserLocation = YES;
//location tracking
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
//Show the users location.. hopefully it works with tracking.
map.showsUserLocation = YES;
[overlay release]; // map is now keeping track of overlay
}
//OverLays Topographical map
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
TileOverlayView *view = [[TileOverlayView alloc] initWithOverlay:overlay];
view.tileAlpha = 0.6;
return [view autorelease];
}
//Tracks Users location and Prints out the Lat and Lon
-(void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
CLLocationCoordinate2D here = newLocation.coordinate;
NSLog(#"%f %f ", here.latitude, here.longitude);
}
The below method focus a particular location in the map,
//Don't forget to add this method to your header also
-(void)focusLocationInMap:(CLLocationCoordinate2D)location
{
MKCoordinateRegion region;
MKCoordinateSpan span;
span.latitudeDelta=0.01;
span.longitudeDelta=0.01;
region.span=span;
region.center=location;
[self.yourMapView setRegion:region animated:TRUE];
[self.yourMapView regionThatFits:region];
}
You could use it anywhere by passing coordinates to it,
CLLocationCoordinate2D here = newLocation.coordinate;
[self focusLocationInMap:here];
here.coordinate is not correct, should be just 'here'
Is there an easy way to set the center and zoom of a google map with the MapView?
In javascript we just do this:
map.setCenter(new google.maps.LatLng(44, -44), 13);
but there is no simple way, other than lots of math, to do this in MapView.
I found a Objective-C answer to this and have translated it for anyone who wants it.
To use, place this in a helper class somewhere and call SetCenterCoordinate()
Source: http://troybrant.net/blog/2010/01/set-the-zoom-level-of-an-mkmapview/
void SetCenterCoordinate (MKMapView MapToCenter, CLLocationCoordinate2D centerCoordinate, int zoomLevel, bool animated)
{
// clamp large numbers to 28
zoomLevel = Math.Min (zoomLevel, 28);
// use the zoom level to compute the region
MKCoordinateSpan span = CoordinateSpanWithMapView (MapToCenter, centerCoordinate, zoomLevel);
MKCoordinateRegion region = new MKCoordinateRegion (centerCoordinate, span);
// set the region like normal
MapToCenter.SetRegion (region, animated);
}
static double MERCATOR_OFFSET = 268435456;
static double MERCATOR_RADIUS = 85445659.44705395;
double LongitudeToPixelSpaceX (double longitude)
{
return Math.Round (MERCATOR_OFFSET + MERCATOR_RADIUS * longitude * Math.PI / 180.0);
}
double LatitudeToPixelSpaceY (double latitude)
{
return Math.Round (MERCATOR_OFFSET - MERCATOR_RADIUS * Math.Log ((1 + Math.Sin (latitude * Math.PI / 180.0)) / (1 - Math.Sin (latitude * Math.PI / 180.0))) / 2.0);
}
double PixelSpaceXToLongitude (double pixelX)
{
return ((Math.Round (pixelX) - MERCATOR_OFFSET) / MERCATOR_RADIUS) * 180.0 / Math.PI;
}
double PixelSpaceYToLatitude (double pixelY)
{
return (Math.PI / 2.0 - 2.0 * Math.Tan (Math.Exp ((Math.Round (pixelY) - MERCATOR_OFFSET) / MERCATOR_RADIUS))) * 180.0 / Math.PI;
}
MKCoordinateSpan CoordinateSpanWithMapView (MKMapView mapView, CLLocationCoordinate2D centerCoordinate, int zoomLevel)
{
// convert center coordiate to pixel space
double centerPixelX = LongitudeToPixelSpaceX (centerCoordinate.Longitude);
double centerPixelY = LatitudeToPixelSpaceY (centerCoordinate.Latitude);
// determine the scale value from the zoom level
int zoomExponent = 20 - zoomLevel;
double zoomScale = Math.Pow (2, zoomExponent);
// scale the map’s size in pixel space
SizeF mapSizeInPixels = mapView.Bounds.Size;
double scaledMapWidth = mapSizeInPixels.Width * zoomScale;
double scaledMapHeight = mapSizeInPixels.Height;
// figure out the position of the top-left pixel
double topLeftPixelX = centerPixelX - (scaledMapWidth / 2);
double topLeftPixelY = centerPixelY - (scaledMapHeight / 2);
// find delta between left and right longitudes
double minLng = PixelSpaceXToLongitude (topLeftPixelX);
double maxLng = PixelSpaceXToLongitude (topLeftPixelX + scaledMapWidth);
double longitudeDelta = maxLng - minLng;
// find delta between top and bottom latitudes
double minLat = PixelSpaceYToLatitude (topLeftPixelY);
double maxLat = PixelSpaceYToLatitude (topLeftPixelY + scaledMapHeight);
double latitudeDelta = -1 * (maxLat - minLat);
// create and return the lat/lng span
MKCoordinateSpan span = new MKCoordinateSpan (latitudeDelta, longitudeDelta);
return span;
}
and another useful one:
public static void CenterMapToAnnotations(MKMapView Map, double latlongPadding)
{
if (Map == null || Map.Annotations == null)
return;
List<MKAnnotation> annotations = new List<MKAnnotation>();
foreach (var a in Map.Annotations)
{
annotations.Add(a as MKAnnotation);
}
if (annotations.Count > 1)
{
double maxLat = annotations.OrderByDescending (e => e.Coordinate.Latitude).FirstOrDefault ().Coordinate.Latitude;
double maxLong = annotations.OrderByDescending (e => e.Coordinate.Longitude).FirstOrDefault ().Coordinate.Longitude;
double minLat = annotations.OrderBy (e => e.Coordinate.Latitude).FirstOrDefault ().Coordinate.Latitude;
double minLong = annotations.OrderBy (e => e.Coordinate.Longitude).FirstOrDefault ().Coordinate.Longitude;
MKCoordinateRegion region = new MKCoordinateRegion ();
region.Center.Latitude = (maxLat + minLat) / 2;
region.Center.Longitude = (maxLong + minLong) / 2;
region.Span.LatitudeDelta = maxLat - minLat+latlongPadding ;
region.Span.LongitudeDelta = maxLong - minLong+latlongPadding;
Map.Region = region;
}
}
-(void) centerMap
{
MKCoordinateRegion region;
CLLocationDegrees maxLat = -90;
CLLocationDegrees maxLon = -180;
CLLocationDegrees minLat = 120;
CLLocationDegrees minLon = 150;
for (int i=0; i<[self.arallData count]; i++) {
Place* home = [[[Place alloc] init] autorelease];
home.latitude = [[[self.arallData objectAtIndex:i] valueForKey:#"Latitude"]floatValue];
home.longitude =[[[self.arallData objectAtIndex:i] valueForKey:#"Longitude"]floatValue];
PlaceMark* from = [[[PlaceMark alloc] initWithPlace:home] autorelease];
CLLocation* currentLocation = (CLLocation*)from ;
if(currentLocation.coordinate.latitude > maxLat)
maxLat = currentLocation.coordinate.latitude;
if(currentLocation.coordinate.latitude < minLat)
minLat = currentLocation.coordinate.latitude;
if(currentLocation.coordinate.longitude > maxLon)
maxLon = currentLocation.coordinate.longitude;
if(currentLocation.coordinate.longitude < minLon)
minLon = currentLocation.coordinate.longitude;
region.center.latitude = (maxLat + minLat) / 2;
region.center.longitude = (maxLon + minLon) / 2;
region.span.latitudeDelta = maxLat - minLat;
region.span.longitudeDelta = maxLon - minLon;
}
[mapView setRegion:region animated:YES];
}