zoukankan      html  css  js  c++  java
  • WGS84坐标转火星坐标(iOS篇)

    --转自:keakon的涂鸦馆 http://www.keakon.net/2011/07/02/WGS84%E5%9D%90%E6%A0%87%E8%BD%AC%E7%81%AB%E6%98%9F%E5

    在这个神奇的国度里,我们总得学习一些有中国特色的东东,例如“火星坐标”。也许有人还不知道这是什么玩意,我就简要介绍一下吧。

    如果你有带GPS模块的智能手机,打开定位功能,然后访问Google地图。只要你身处中国大陆,你就会发现定位不准,大概有几百米的偏差。然而运行一些导航软件,你又会发现定位很准确,说明手机的GPS模块确实是正常的。
    这 种现象是怎么造成的呢?答案是人为造成的。简单来说,GPS模块获取到的坐标是WGS84坐标系的,中国政府出于种种目的的考虑,不允许中国的地图使用国 际通用的WGS84坐标系,而非要加上一些偏移,这样的坐标系就俗称“火星坐标系”。而Google地图采用的也是加偏移过的火星坐标系,但GPS模块传 给它的坐标却没有加偏移,于是就出现几百米的偏差了。

    这样的后果就是没法做需要很高精度的地理位置的应用了,就像那个悲剧的Google地图一样,你迷路时无法指望它告诉你正确的位置。
    不 过上有政策,下有对策。首先要说的是不加偏的地图肯定是没有的,就算有也是机密,只能掌握在美国佬手里。所以只能考虑如何将WGS84坐标转换成火星坐标 了。遗憾的是这种转换不是线性的,没有一个简单的换算公式,而且每个地图提供商采用的加偏算法都不相同(也就是有很多种火星坐标系)。
    然而勤劳智 慧的中国屁民们很快就找到了解决办法:用大量的采样点覆盖这960万平方公里的土地,把对应的WGS84坐标和火星坐标一一记录起来,做成一个偏移数据 库,然后直接查询即可。缺点就是统计麻烦,随着精度的提高,数据库占用的空间也会增大,而且地图商一换坐标系(一般几年一次),数据库就失效了。
    还有一些更聪明的人就用matlab来分析这些采样点,拟合出了加偏函数的曲线,然后直接计算即可。

    不过我属于那种很懒又不会matlab的人,所以上述方案都被我否决了(还有个原因就是那样做是违法的)。
    后来我无意中想起Google地图的数据来自MapABC,何不去MapABC看看呢?于是搜索了一下,发现了MapABC地图API。在MapABC API for iOS文档里我找到了GPSToOffSetByPoint:方法,用途就是查询偏移后的坐标!

    于是赶紧注册账号,获取移动版API KEY,并下载iOS API SDK和文档。搞定这些后就可以开启Xcode来试验了。

    先做个获取WGS84坐标的应用出来:
    //  MapDemoViewController.h
    
    #import <UIKit/UIKit.h>
    #import <CoreLocation/CoreLocation.h>
    
    @interface MapDemoViewController : UIViewController <CLLocationManagerDelegate> {
        UITextField *wgsText;
        UITextField *marsText;
        CLLocationManager *locationManager;
    }
    
    @property (nonatomic, retain) IBOutlet UITextField *wgsText;
    @property (nonatomic, retain) IBOutlet UITextField *marsText;
    @property (nonatomic, retain) CLLocationManager *locationManager;
    
    @end

    //  MapDemoViewController.m
    
    #import "MapDemoViewController.h"
    
    @implementation MapDemoViewController
    
    @synthesize wgsText;
    @synthesize marsText;
    @synthesize locationManager;
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        if ([CLLocationManager locationServicesEnabled]) { // 检查定位服务是否可用
            locationManager = [[CLLocationManager alloc] init];
            locationManager.delegate = self;
            [locationManager startUpdatingLocation]; // 开始定位
        }
    }
    
    // 定位成功时调用
    - (void)locationManager:(CLLocationManager *)manager
        didUpdateToLocation:(CLLocation *)newLocation
               fromLocation:(CLLocation *)oldLocation {
        CLLocationCoordinate2D coordinate = newLocation.coordinate;
        CLLocationDegrees latitude = coordinate.latitude;
        CLLocationDegrees longitude = coordinate.longitude;
        wgsText.text = [NSString stringWithFormat:@"%f,%f", latitude, longitude];
    }
    
    // 定位失败时调用
    - (void)locationManager:(CLLocationManager *)manager
           didFailWithError:(NSError *)error {
        wgsText.text = [error localizedDescription];
        marsText.text = @"";
    }
    
    - (void)viewDidUnload {
        self.wgsText = nil;
        self.marsText = nil;
        self.locationManager = nil;
    }
    
    - (void)dealloc {
        [wgsText release];
        [marsText release];
        [locationManager release];
        [super dealloc];
    }
    
    @end
    构建前别忘了在Framework里加上CoreLocation.framework。这个例子只能在真机上运行,因为模拟器没有定位功能,肯定会失败。
    如果没出现什么差错的话,你可以看到wgsText对应的文本框里会显示你的WGS84坐标了,不过marsText还是空白。

    接下来把SDK中的include文件夹复制到项目的Classes文件夹下,再创建一个lib文件夹,把libiMapSDKLib.a复制进去(因为模拟器没用,所以直接复制release版的),再将这些文件全部添加到项目里。
    接着在Framework里加上libstdc++.dylib(一共有5个,貌似随便选一个即可)。
    然 后打开项目设置,把“Other Linker Flag”设为“-liMapSDKLib”(也就是去掉库文件名前后的lib和.a,并在前面加上-l参数)。再将“Header Search Path”设为“$(SRCROOT)/include”,Recursive打钩;“Library Search Path”设为“$(SRCROOT)/lib”。
    这样设置就完成了,build一下,如果没error就表示正常。

    接下来要创建一个MSearch对象,由于需要持久存在,将其作为MapDemoViewController的属性。
    而这个对象的delegate属性需要实现MSearchDelegate和GPSToOffsetByPoint_Delegate协议,于是也给MapDemoViewController加上。
    GPSToOffsetByPoint_Delegate协议还包含了MLONLAT poiXY和MCOORDINATESEARCHOPTIONS options这2个属性(都是结构,无需retain),因此也得加上。
    最后别忘了加上“#import "MSearch.h"”。

    定义完成后,就可以开始创建MSearch对象了。这个对象初始化时需要与MapABC服务器通信,验证API KEY是否有效。
    这个验证过程是异步的,当验证完成后才能使用它,因此当检查到定位服务可用时,就可以立刻创建它了:
    self.search = [MSearch MSearchWithKey:@"你申请的API KEY" delegate:self];

    而在获取到WGS84坐标后,就可以查询火星坐标了:
    poiXY.X = longitude; // 经度
    poiXY.Y = latitude; // 维度
    [search GPSToOffSetByPoint:self];
    这里不需要设置options属性,使用初始值即可。GPSToOffSetByPoint:方法会返回SEARCH_THREAD_ID,由于不需要停止线程,因此无视。

    当获取到火星坐标后,就会调用GPSToOffsetResponse:方法了。这个方法的lonlat参数就是火星坐标了:
    -(void) GPSToOffsetResponse:(MLONLAT)lonlat {
        marsText.text = [NSString stringWithFormat:@"%f,%f", lonlat.Y, lonlat.X];
    }

    现在build一下,你会发现出来一堆error了。一看发现是无法识别template语法,于是把2个.m文件重命名成.mm文件,使其可以混用C++。
    再 次build,仍然会出现“___restore_vfp_d8_d15_regs”之类完全看不懂的error。不过最后一行有提到 “symbol(s) not found for architecture armv6”什么的,于是在项目设置里改成只编译ARMv7的,这下终于成功了。(支持ARMv6的解决办法暂时没找到,不知道是不是这库的限制。)

    于是立刻尝试了一番,检验是否有效。
    先用iPhone自带的地图应用定位,位置准确:

    再用刚写的应用来定位,找到火星坐标:

    最后到Google地图搜索这个火星坐标,与地图应用的定位吻合:


    OK,大功告成,欢迎来到火星~


    2011年11月27日更新:

    刚发现MKLocationManager这个私有类有个_applyChinaLocationShift方法可以解决偏移,于是尝试了一番:
    #import <UIKit/UIKit.h>
    #import <CoreLocation/CoreLocation.h>
    
    @class MKLocationManager;
    
    @interface ViewController : UIViewController <CLLocationManagerDelegate> {
        UITextField *wgsText;
        UITextField *marsText;
        CLLocationManager *locationManager;
        MKLocationManager *shiftLocationManager;
    }
    
    @property (nonatomic, retain) IBOutlet UITextField *wgsText;
    @property (nonatomic, retain) IBOutlet UITextField *marsText;
    @property (nonatomic, retain) CLLocationManager *locationManager;
    @property (nonatomic, retain) MKLocationManager *shiftLocationManager;
    
    @end
    
    
    #import <MapKit/MapKit.h>
    #import "ViewController.h"
    
    @interface MKLocationManager : NSObject
    - (CLLocation*)_applyChinaLocationShift:(CLLocation*)arg;
    - (BOOL)chinaShiftEnabled;
    + (id)sharedLocationManager;
    @end
    
    @implementation ViewController
    
    @synthesize wgsText;
    @synthesize marsText;
    @synthesize locationManager;
    @synthesize shiftLocationManager;
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        if ([CLLocationManager locationServicesEnabled]) {
            locationManager = [[CLLocationManager alloc] init];
            locationManager.delegate = self;
            [locationManager startUpdatingLocation];
            
            self.shiftLocationManager = MKLocationManager.sharedLocationManager;
            if (shiftLocationManager.chinaShiftEnabled) {
                [[[MKMapView alloc] init] release]; // 必须初始化一个MKMapView对象,否则调用_applyChinaLocationShift:时会出错
            } else {
                self.shiftLocationManager = nil;
            }
        }
    }
    
    - (void)locationManager:(CLLocationManager *)manager
        didUpdateToLocation:(CLLocation *)newLocation
               fromLocation:(CLLocation *)oldLocation {
        CLLocationCoordinate2D coordinate = newLocation.coordinate;
        wgsText.text = [NSString stringWithFormat:@"%f,%f", coordinate.latitude, coordinate.longitude];
    
        if (shiftLocationManager) {
            newLocation = [shiftLocationManager _applyChinaLocationShift:newLocation]; // 获得偏移后的坐标
            coordinate = newLocation.coordinate;
            marsText.text = [NSString stringWithFormat:@"%f,%f", coordinate.latitude, coordinate.longitude];
        } 
    }
    
    // ...
    
    @end
    接着添加CoreLocation和MapKit这2个框架,就可以构建了。
    测 试时发现这个函数也需要联网,否则拿到的坐标为{0.0,0.0};不过获取到一次后,就可以断开网络了,这时候也能正常地偏移,而且不是线性的,看来是 有内置的算法。经嗅探发现是要往http://www.google.cn/glm/mmap发送2次POST请求,不过都是二进制的数据,看不懂。
    不过和其他私有API一样,模拟器上是没法用的,而且要冒被Apple拒绝的危险。
    最后,据说iOS 5上要改用GEOLocationShiftProvider,不过我找不到相应资料,完全不会用…而且因为要使用GMM这个私有框架,很容易被Apple查出来。
  • 相关阅读:
    bugku细心地大象
    【学术篇】一些水的不行的dp
    【笔记篇】莫队算法(一)
    【学术篇】luogu1351 [NOIP2014提高组] 联合权值
    【学术篇】网络流24题——方格取数加强版
    【学术篇】SDOI2009 SuperGCD
    【学术篇】网络流24题——方格取数问题
    【模板篇】A* 寻路算法
    【模板篇】k短路 SDOI2010 魔法猪学院
    【学术篇】SDOI2009 最优图像
  • 原文地址:https://www.cnblogs.com/chenfulai/p/2441917.html
Copyright © 2011-2022 走看看