先看效果和图:
代码中引用了github下载的代码.主要引用了其中的
headingInDegrees方法.可以将2点的坐标计算出相对角度
地址:https://github.com/progrmr/SDK_Utilities
UtilitiesGeo.h
/*
* UtilitiesGeo.h
*
* Created by Gary Morris on 3/6/11.
* Copyright 2011 Gary A. Morris. All rights reserved.
*
* This file is part of SDK_Utilities.repo
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this file. If not, see <http://www.gnu.org/licenses/>.
*/
// distance conversion factors
#define MILES_TO_METERS_FACTOR (1609.344) /* exact */
#define MILES_TO_FEET_FACTOR (5280) /* exact */
#define METERS_TO_MILES_FACTOR (1/MILES_TO_METERS_FACTOR)
#define METERS_TO_FEET_FACTOR (3.2808399)
#define NM_TO_METERS_FACTOR (1852) /* exact */
#define NM_TO_FEET_FACTOR (6076.11549) /* approx */
// earth radius is for a spherical earth model
#define EARTH_RADIUS_METERS (6372797.6)
#define SPEED_OF_LIGHT_MPS (299792458) /* exact */
// degrees/radians conversion macros
#define Deg_to_Rad(X) (X*M_PI/180.0)
#define Rad_to_Deg(X) (X*180.0/M_PI)
/*-------------------------------------------------------------------------
* Given two lat/lon points on earth, calculates the heading
* from lat1/lon1 to lat2/lon2.
*
* lat/lon params in radians
* result in radians
*-------------------------------------------------------------------------*/
double headingInRadians(double lat1, double lon1, double lat2, double lon2);
/*-------------------------------------------------------------------------
* Given two lat/lon points on earth, calculates the heading
* from lat1/lon1 to lat2/lon2.
*
* lat/lon params in degrees
* result in degrees
*-------------------------------------------------------------------------*/
double headingInDegrees(double lat1, double lon1, double lat2, double lon2);
/*-------------------------------------------------------------------------
* Given a starting lat/lon point on earth, distance (in meters)
* and bearing, calculates destination coordinates lat2/lon2.
*
* all params in radians
*-------------------------------------------------------------------------*/
void destCoordsInRadians(double lat1, double lon1,
double distanceMeters, double bearing,
double* lat2, double* lon2);
/*-------------------------------------------------------------------------
* Given a starting lat/lon point on earth, distance (in meters)
* and bearing, calculates destination coordinates lat2/lon2.
*
* all params in degrees
*-------------------------------------------------------------------------*/
void destCoordsInDegrees(double lat1, double lon1,
double distanceMeters, double bearing,
double* lat2, double* lon2);
// Normalize a heading in degrees to be within -179.999999° to 180.00000°
double normalize180(double heading);
float normalize180f(float heading);
// Normalize a heading in degrees to be within 0° to 359.999999°
double normalize360(double heading);
float normalize360f(float heading);
UtilitiesGeo.c
/*
* UtilitiesGeo.h
*
* Created by Gary Morris on 3/6/11.
* Copyright 2011 Gary A. Morris. All rights reserved.
*
* This file is part of SDK_Utilities.repo
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "UtilitiesGeo.h"
#include <math.h>
/*-------------------------------------------------------------------------
* Given a starting lat/lon point on earth, distance (in meters)
* and bearing, calculates destination coordinates lat2/lon2.
*
* all params in radians
*-------------------------------------------------------------------------*/
void destCoordsInRadians(double lat1, double lon1,
double distanceMeters, double bearing,
double* lat2, double* lon2)
{
//-------------------------------------------------------------------------
// Algorithm from http://www.geomidpoint.com/destination/calculation.html
// Algorithm also at http://www.movable-type.co.uk/scripts/latlong.html
//
// Spherical Earth Model
// 1. Let radiusEarth = 6372.7976 km or radiusEarth=3959.8728 miles
// 2. Convert distance to the distance in radians.
// dist = dist/radiusEarth
// 3. Calculate the destination coordinates.
// lat2 = asin(sin(lat1)*cos(dist) + cos(lat1)*sin(dist)*cos(brg))
// lon2 = lon1 + atan2(sin(brg)*sin(dist)*cos(lat1), cos(dist)-sin(lat1)*sin(lat2))
//-------------------------------------------------------------------------
const double distRadians = distanceMeters / EARTH_RADIUS_METERS;
*lat2 = asin( sin(lat1) * cos(distRadians) + cos(lat1) * sin(distRadians) * cos(bearing));
*lon2 = lon1 + atan2( sin(bearing) * sin(distRadians) * cos(lat1),
cos(distRadians) - sin(lat1) * sin(*lat2) );
}
/*-------------------------------------------------------------------------
* Given a starting lat/lon point on earth, distance (in meters)
* and bearing, calculates destination coordinates lat2/lon2.
*
* all params in degrees
*-------------------------------------------------------------------------*/
void destCoordsInDegrees(double lat1, double lon1,
double distanceMeters, double bearing,
double* lat2, double* lon2)
{
destCoordsInRadians(Deg_to_Rad(lat1), Deg_to_Rad(lon1),
distanceMeters, Deg_to_Rad(bearing),
lat2, lon2);
*lat2 = Rad_to_Deg( *lat2 );
*lon2 = normalize180( Rad_to_Deg( *lon2 ) );
}
/*-------------------------------------------------------------------------
* Given two lat/lon points on earth, calculates the heading
* from lat1/lon1 to lat2/lon2.
*
* lat/lon params in radians
* result in radians
*-------------------------------------------------------------------------*/
double headingInRadians(double lat1, double lon1, double lat2, double lon2)
{
//-------------------------------------------------------------------------
// Algorithm found at http://www.movable-type.co.uk/scripts/latlong.html
//
// Spherical Law of Cosines
//
// Formula: θ = atan2( sin(Δlon) * cos(lat2),
// cos(lat1) * sin(lat2) − sin(lat1) * cos(lat2) * cos(Δlon) )
// JavaScript:
//
// var y = Math.sin(dLon) * Math.cos(lat2);
// var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
// var brng = Math.atan2(y, x).toDeg();
//-------------------------------------------------------------------------
double dLon = lon2 - lon1;
double y = sin(dLon) * cos(lat2);
double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon);
return atan2(y, x);
}
/*-------------------------------------------------------------------------
* Given two lat/lon points on earth, calculates the heading
* from lat1/lon1 to lat2/lon2.
*
* lat/lon params in degrees
* result in degrees
*-------------------------------------------------------------------------*/
double headingInDegrees(double lat1, double lon1, double lat2, double lon2)
{
return Rad_to_Deg( headingInRadians(Deg_to_Rad(lat1),
Deg_to_Rad(lon1),
Deg_to_Rad(lat2),
Deg_to_Rad(lon2)) );
}
// Normalize a heading in degrees to be within -179.999999° to 180.00000°
double normalize180(double heading)
{
while (1) {
if (heading <= -180) {
heading += 360;
} else if (heading > 180) {
heading -= 360;
} else {
return heading;
}
}
}
// Normalize a heading in degrees to be within -179.999999° to 180.00000°
float normalize180f(float heading)
{
while (1) {
if (heading <= -180) {
heading += 360;
} else if (heading > 180) {
heading -= 360;
} else {
return heading;
}
}
}
// Normalize a heading in degrees to be within 0° to 359.999999°
double normalize360(double heading)
{
while (1) {
if (heading < 0) {
heading += 360;
} else if (heading >= 360) {
heading -= 360;
} else {
return heading;
}
}
}
// Normalize a heading in degrees to be within 0° to 359.999999°
float normalize360f(float heading)
{
while (1) {
if (heading < 0) {
heading += 360;
} else if (heading >= 360) {
heading -= 360;
} else {
return heading;
}
}
}
main.m
//
// main.m
// ControlDemo
//
// Created by watsy0007 on 12-6-3.
// Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#import "UtilitiesGeo.h"
@interface CWUserLocation : NSObject <MKAnnotation>
@property (nonatomic) CLLocationCoordinate2D coord;
@end
@implementation CWUserLocation
@synthesize coord = _coord;
- (CLLocationCoordinate2D) coordinate {
return _coord;
}
- (void) setCoordinate:(CLLocationCoordinate2D)newCoordinate {
_coord = newCoordinate;
}
- (NSString *) title {
return @"title";
}
- (NSString *) subtitle {
return @"subtitle";
}
@end
@interface ViewController : UIViewController <MKMapViewDelegate,CLLocationManagerDelegate> {
CLLocationManager *_locMgr;
UILabel *_label;
MKMapView *_mapView;
}
@end
@implementation ViewController
- (void) dealloc {
[_label release];
[_mapView release];
[super dealloc];
}
//随机生成1些标注位置
- (NSArray *) randomAnnotation:(NSInteger) nMax {
random();
NSMutableArray *annotationArray = [NSMutableArray array];
for (int i = 0; i < nMax; i++) {
CWUserLocation *place = [[CWUserLocation alloc] init];
double dRandlatitude = (double)(rand() % 10000) / 1000000;
double dRandlongitude = (double)(rand() % 10000) / 1000000;
BOOL bAdd = rand() % 2;
if (bAdd) {
dRandlatitude = -dRandlatitude;
}
bAdd = rand() % 2;
if (bAdd) {
dRandlongitude = -dRandlongitude;
}
[place setCoordinate:CLLocationCoordinate2DMake(22.551368 + dRandlatitude, 113.882654 + dRandlongitude)];
[annotationArray addObject:place];
[place release];
}
return annotationArray;
}
- (void) updateAngle {
NSArray *annoArray = [_mapView annotations];
CWUserLocation *place = [annoArray objectAtIndex:0];
MKMapPoint pUser = MKMapPointForCoordinate(_mapView.userLocation.location.coordinate);
MKMapPoint pMapPoint = MKMapPointForCoordinate(place.coordinate);
if (MKMapPointEqualToPoint(pUser,pMapPoint)) {
place = [annoArray objectAtIndex:1];
}
//计算出地图上2点的角度.正北为0度
CGFloat fAngle = headingInDegrees(_mapView.userLocation.coordinate.latitude, _mapView.userLocation.coordinate.longitude,
place.coordinate.latitude, place.coordinate.longitude);
// NSLog(@"\n%lf|%lf - %lf|%lf\n %.1f \n%.1f %.1f %.1f",_mapView.userLocation.coordinate.latitude, _mapView.userLocation.coordinate.longitude,
// place.coordinate.latitude, place.coordinate.longitude,fAngle,_locMgr.heading.magneticHeading,_locMgr.heading.trueHeading,_locMgr.heading.headingAccuracy);
//减去磁北偏差角度.得到在地图上的正确角度.
_label.transform = CGAffineTransformMakeRotation((fAngle - _locMgr.heading.magneticHeading) / 180.0 * M_PI);
}
- (void) randomAnno {
[_mapView removeAnnotations:_mapView.annotations];
[_mapView addAnnotations:[self randomAnnotation:1]];
[self updateAngle];
}
- (void) loadView {
[super loadView];
[[self view] setBackgroundColor:[UIColor whiteColor]];
UIBarButtonItem *barItem = [[UIBarButtonItem alloc] initWithTitle:@"随机" style:UIBarButtonItemStylePlain target:self action:@selector(randomAnno)];
self.navigationItem.rightBarButtonItem = barItem;
[barItem release];
_mapView = [[MKMapView alloc] initWithFrame:self.view.bounds];
[_mapView setShowsUserLocation:YES];
[_mapView setDelegate:self];
[self.view addSubview:_mapView];
[self randomAnno];
_mapView.userTrackingMode = MKUserTrackingModeFollowWithHeading;
_label = [[UILabel alloc] init];
_label.text = @"^";
_label.textAlignment = UITextAlignmentCenter;
_label.frame = self.view.bounds;
[_label setBackgroundColor:[UIColor clearColor]];
[self.view addSubview:_label];
_locMgr = [[CLLocationManager alloc] init];
_locMgr.delegate = self;
if([CLLocationManager headingAvailable]) {
[_locMgr startUpdatingHeading];
}
if ([CLLocationManager locationServicesEnabled]) {
[_locMgr startUpdatingLocation];
}
}
- (void) viewWillUnload {
[_label release];
[super viewWillUnload];
}
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return YES;
}
- (void) viewDidLoad {
[super viewDidLoad];
}
#pragma mark -
#pragma mark cllocationmanager delegate
- (void) locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading {
NSLog(@"\n地磁北极:%.0f\n真正北极:%.0f\n偏移%.0f\n记录时间:%@\nx:%.1f y:%.1f z:%.1f",
newHeading.magneticHeading,
newHeading.trueHeading,
newHeading.headingAccuracy,
newHeading.timestamp,
newHeading.x,newHeading.y,newHeading.z);
}
#pragma mark -
#pragma mark 地图委托
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *sAnno = @"sAnnoString";
MKPinAnnotationView *annoView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:sAnno];
if (annoView == nil) {
if (annotation == mapView.userLocation) {
annoView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:sAnno] autorelease];
annoView.pinColor = MKPinAnnotationColorGreen;
} else {
annoView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:sAnno] autorelease];
annoView.pinColor = MKPinAnnotationColorRed;
}
}
return annoView;
}
//更新用户位置
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
// NSLog(@"%@",@"didUpdateUserLocation");
[self updateAngle];
}
@end
//-----------------------------------------------------------------------------------------------------
#pragma mark -
#pragma mark AppDelegate
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UIViewController *viewController;
@end
@implementation AppDelegate
@synthesize window = _window;
@synthesize viewController = _viewController;
- (void) dealloc {
[_window release];
[_viewController release];
[super dealloc];
}
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
srand(time(NULL));
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
self.viewController = [[ViewController alloc] init];
UINavigationController *controller = [[UINavigationController alloc] initWithRootViewController:self.viewController];
self.window.rootViewController = controller;
[controller release];
[self.window makeKeyAndVisible];
return YES;
}
@end
int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}