眾所周知地球是一個不規(guī)則橢圓體,GIS中的坐標(biāo)系定義由基準(zhǔn)面和地圖投影兩組參數(shù)確定,而基準(zhǔn)面的定義則由特定橢球體及其對應(yīng)的轉(zhuǎn)換參數(shù)確定。 基準(zhǔn)面是利用特定橢球體對特定地區(qū)地球表面的逼近,因此每個國家或地區(qū)均有各自的基準(zhǔn)面。基準(zhǔn)面是在橢球體基礎(chǔ)上建立的,橢球體可以對應(yīng)多個基準(zhǔn)面,而基準(zhǔn)面只能對應(yīng)一個橢球體。 意思就是無論是谷歌地圖、搜搜地圖還是高德地圖、百度地圖區(qū)別只是針對不同的大地地理坐標(biāo)系標(biāo)準(zhǔn)制作的經(jīng)緯度,不存在準(zhǔn)不準(zhǔn)的問題,大家都是準(zhǔn)的只是參照物或者說是標(biāo)準(zhǔn)不一樣。
谷歌地圖采用的是WGS84地理坐標(biāo)系(中國范圍除外),谷歌中國地圖和搜搜中國地圖采用的是GCJ02地理坐標(biāo)系,百度采用的是BD09坐標(biāo)系,而設(shè)備一般包含GPS芯片或者北斗芯片獲取的經(jīng)緯度為WGS84地理坐標(biāo)系,為什么不統(tǒng)一用WGS84地理坐標(biāo)系這就是國家地理測繪總局對于出版地圖的要求,出版地圖必須符合GCJ02坐標(biāo)系標(biāo)準(zhǔn)了,也就是國家規(guī)定不能直接使用WGS84地理坐標(biāo)系。所以定位大家感覺不準(zhǔn)確很多又叫出版地圖為火星地圖其實只是坐標(biāo)系不一樣而已。
這就是為什么設(shè)備采集的經(jīng)緯度在地圖上顯示的時候經(jīng)常有很大的偏差,遠(yuǎn)遠(yuǎn)超出民用GPS 10米偏移量的技術(shù)規(guī)范,于是我們就有了谷歌地圖糾偏 騰訊搜搜糾偏 混合地圖糾偏 百度谷歌互轉(zhuǎn)存在的價值。
那如何對谷歌地圖糾偏、搜搜soso地圖糾偏或者對百度地圖糾偏呢,如果用算法目前沒有太好的算法直接轉(zhuǎn)換,所以大家采用的都是比對的方法吧地球劃分成若干個小塊找到地圖的偏差量記錄下來,然后根據(jù)任意經(jīng)緯度找尋最接近的偏差量加上偏差量就可以實現(xiàn)不同地圖之間的經(jīng)緯度轉(zhuǎn)換。現(xiàn)在有0.01度糾偏經(jīng)緯度信息,可以提供任意格式,可以直接把經(jīng)緯度偏移量調(diào)整回來。
百度地圖糾偏信息包含中國海域一共29,699,997條糾偏數(shù)據(jù),谷歌地圖只包含中國陸地一共12,597,551條糾偏數(shù)據(jù),基站數(shù)據(jù)移動和聯(lián)通的共340萬數(shù)據(jù)。
移動聯(lián)通基站數(shù)據(jù)字段說明: MCC:國家 (460是中國) MNC:0是移動,1是聯(lián)通 LAC:小區(qū)號 CELL:基站號 LNG:緯度 LAT:經(jīng)度 O_LNG:糾偏后的緯度(用于google地圖顯示) O_LAT:糾偏后的經(jīng)度(用于google地圖顯示)PRECISION:基站半徑范圍單位米 ADDRESS:詳細(xì)地址中文描述 REGION:省份 CITY:城市 COUNTRY:國家
經(jīng)研究發(fā)現(xiàn),百度地圖的坐標(biāo)系為BD09,高德地圖坐標(biāo)為GCJ02,這樣就存在不同坐標(biāo)系的坐標(biāo)之間轉(zhuǎn)換的問題了,查api吧,然后又發(fā)現(xiàn)無論百度地圖還是高德地圖,api列表里都沒有提這個事情。
而因為我是展示的百度地圖,有要獲取百度地圖上的中心點的經(jīng)緯度然后在作為參數(shù)調(diào)用高德地圖api的需求,而百度地圖并未提供bd09坐標(biāo)系轉(zhuǎn)出的api(也可以理解,因為如果這樣的話,相當(dāng)于沒加密啊,自己將加密算法寫出來,再給個解密的api不是有毛病么,不能不把國家的規(guī)定放眼里嘛,百度也說有深層次的需求要以公司名義給他們發(fā)郵件什么的,但是一般來說一個小iOS項目客戶不會那么興師動眾,而且據(jù)群眾反映,郵件的效果也不怎么好),所以一時做了罷。
但是柳暗花明又一村了,雖然無法將百度地圖上的任意點轉(zhuǎn)為gcj02坐標(biāo)系的點,但是百度地圖還是在他們的定位api里提供了方法,使得應(yīng)用在獲取當(dāng)前位置的時候,可以獲取以“gcj02”為坐標(biāo)系的點:
這樣以來,就可以用高德地圖獲取位置信息了,雖然說只能有當(dāng)前位置這一個點是取成gcj02坐標(biāo)的點是比較囧的o(╯□╰)o ,還有,別忘了,取得的當(dāng)前點要轉(zhuǎn)化成bd09的坐標(biāo)系之后,再展示在百度地圖上,不然是會有偏差的。
另外比較一下百度地圖和高德地圖(因為害怕谷歌地圖在大陸地區(qū)的服務(wù)受限問題等等,所以沒考慮使用谷歌地圖,也就沒怎么研究)。
百度地圖在頁面上的展示方面做的還是很好的,包括頁面的縮放,信息的標(biāo)注等等,相比之下高德地圖就會在某些安卓版本的某些機(jī)器上出現(xiàn)在放縮的時候地圖信息展示的不夠清晰不夠明確的情況,而且在定位時,百度的地位相對準(zhǔn)確,因為我們公司的網(wǎng)絡(luò)服務(wù)器不在公司辦公所在地,所以高德地圖有時候定位就定位到服務(wù)器的地址去了,百度從我使用至今還沒出現(xiàn)過這樣的錯誤。
但是百度地圖在poi搜索這一塊,在我看來是相對薄弱的,百度地圖的poi搜索在不輸入關(guān)鍵字的時候,是不能做模糊搜索的,而且也不能根據(jù)類型搜索(比如僅搜索飲食,搜索學(xué)校之類的),而在高德地圖里這些就做到了。而且在逆地理編碼時,高德地圖獲取的結(jié)果是相對比百度地圖更豐富的。
WGS84坐標(biāo)系:即地球坐標(biāo)系,國際上通用的坐標(biāo)系。
GCJ02坐標(biāo)系:即火星坐標(biāo)系,WGS84坐標(biāo)系經(jīng)加密后的坐標(biāo)系。
BD09坐標(biāo)系:即百度坐標(biāo)系,GCJ02坐標(biāo)系經(jīng)加密后的坐標(biāo)系。
搜狗坐標(biāo)系、圖吧坐標(biāo)系等,估計也是在GCJ02基礎(chǔ)上加密而成的。
注1:百度地圖使用百度坐標(biāo),支持從地球坐標(biāo)和火星坐標(biāo)導(dǎo)入成百度坐標(biāo),但無法導(dǎo)出。并且批量坐標(biāo)轉(zhuǎn)換一次只能轉(zhuǎn)換20個(待驗證)。
注2:搜狗地圖支持直接顯示地球坐標(biāo),支持地球坐標(biāo)、火星坐標(biāo)、百度坐標(biāo)導(dǎo)入成搜狗坐標(biāo),同樣,搜狗坐標(biāo)也無法導(dǎo)出。
地球坐標(biāo)轉(zhuǎn)為火星坐標(biāo)
const double ee = 0.00669342162296594323;
+ (CLLocation *)transformToMars:(CLLocation *)location {
//是否在中國大陸之外
if ([[self class] outOfChina:location]) {
return location;
}
double dLat = [[self class] transformLatWithX:location.coordinate.longitude - 105.0 y:location.coordinate.latitude - 35.0];
double dLon = [[self class] transformLonWithX:location.coordinate.longitude - 105.0 y:location.coordinate.latitude - 35.0];
double radLat = location.coordinate.latitude / 180.0 * M_PI;
double magic = sin(radLat);
magic = 1 - ee * magic * magic;
double sqrtMagic = sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * M_PI);
dLon = (dLon * 180.0) / (a / sqrtMagic * cos(radLat) * M_PI);
return [[CLLocation alloc] initWithLatitude:location.coordinate.latitude + dLat longitude:location.coordinate.longitude + dLon];
}
+ (BOOL)outOfChina:(CLLocation *)location {
if (location.coordinate.longitude < 72.004 || location.coordinate.longitude > 137.8347) {
return YES;
}
if (location.coordinate.latitude < 0.8293 || location.coordinate.latitude > 55.8271) {
return YES;
}
return NO;
}
+ (double)transformLatWithX:(double)x y:(double)y {
double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(abs(x));
ret += (20.0 * sin(6.0 * x * M_PI) + 20.0 * sin(2.0 * x * M_PI)) * 2.0 / 3.0;
ret += (20.0 * sin(y * M_PI) + 40.0 * sin(y / 3.0 * M_PI)) * 2.0 / 3.0;
ret += (160.0 * sin(y / 12.0 * M_PI) + 320.0 * sin(y * M_PI / 30.0)) * 2.0 / 3.0;
return ret;
}
+ (double)transformLonWithX:(double)x y:(double)y {
double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(abs(x));
ret += (20.0 * sin(6.0 * x * M_PI) + 20.0 * sin(2.0 * x * M_PI)) * 2.0 / 3.0;
ret += (20.0 * sin(x * M_PI) + 40.0 * sin(x / 3.0 * M_PI)) * 2.0 / 3.0;
ret += (150.0 * sin(x / 12.0 * M_PI) + 300.0 * sin(x / 30.0 * M_PI)) * 2.0 / 3.0;
return ret;
}
4火星坐標(biāo)和地球坐標(biāo)轉(zhuǎn)換
// World Geodetic System ==> Mars Geodetic System(地球坐標(biāo)轉(zhuǎn)化為火星坐標(biāo))
+ (CLLocationCoordinate2D)WorldGS2MarsGS:(CLLocationCoordinate2D)coordinate
{
// a = 6378245.0, 1/f = 298.3
// b = a * (1 - f)
// ee = (a^2 - b^2) / a^2;
const double a = 6378245.0;
const double ee = 0.00669342162296594323;
if (outOfChina(coordinate.latitude, coordinate.longitude))
{
return coordinate;
}
double wgLat = coordinate.latitude;
double wgLon = coordinate.longitude;
double dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
double dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
double radLat = wgLat / 180.0 * PI;
double magic = sin(radLat);
magic = 1 - ee * magic * magic;
double sqrtMagic = sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);
dLon = (dLon * 180.0) / (a / sqrtMagic * cos(radLat) * PI);
return CLLocationCoordinate2DMake(wgLat + dLat, wgLon + dLon);
}
// Mars Geodetic System ==> World Geodetic System(火星坐標(biāo)轉(zhuǎn)為地球坐標(biāo))
+ (CLLocationCoordinate2D)MarsGS2WorldGS:(CLLocationCoordinate2D)coordinate
{
double gLat = coordinate.latitude;
double gLon = coordinate.longitude;
CLLocationCoordinate2D marsCoor = [ALDGeocoder WorldGS2MarsGS:coordinate];
double dLat = marsCoor.latitude - gLat;
double dLon = marsCoor.longitude - gLon;
return CLLocationCoordinate2DMake(gLat - dLat, gLon - dLon);
}
1.GCJ-02(火星坐標(biāo)系)和BD-09轉(zhuǎn)換
// GCJ-02 坐標(biāo)轉(zhuǎn)換成 BD-09 坐標(biāo)
+ (CLLocationCoordinate2D)MarsGS2BaiduGS:(CLLocationCoordinate2D)coordinate
{
double x_pi = PI * 3000.0 / 180.0;
double x = coordinate.longitude, y = coordinate.latitude;
double z = sqrt(x * x + y * y) + 0.00002 * sin(y * x_pi);
double theta = atan2(y, x) + 0.000003 * cos(x * x_pi);
double bd_lon = z * cos(theta) + 0.0065;
double bd_lat = z * sin(theta) + 0.006;
return CLLocationCoordinate2DMake(bd_lat, bd_lon);
}
// BD-09 坐標(biāo)轉(zhuǎn)換成 GCJ-02 坐標(biāo)
+ (CLLocationCoordinate2D)BaiduGS2MarsGS:(CLLocationCoordinate2D)coordinate
{
double x_pi = PI * 3000.0 / 180.0;
double x = coordinate.longitude - 0.0065, y = coordinate.latitude - 0.006;
double z = sqrt(x * x + y * y) - 0.00002 * sin(y * x_pi);
double theta = atan2(y, x) - 0.000003 * cos(x * x_pi);
double gg_lon = z * cos(theta);
double gg_lat = z * sin(theta);
return CLLocationCoordinate2DMake(gg_lat, gg_lon);
}