公司app开发过程中需要用到定位功能,在这里对一些定位的常识做一下记录。
平台区别
- IOS平台都是通过系统SDK接口获取的,因此所有App获取定位及精度的能力是相同的,即使Google Maps、百度地图、高德地图这种专业地图App也是如此。
- Android平台由于Google Service被阉割,国内App通常是通过高德、百度等第三方SDK接口获取定位信息,因此在定位能力和精度上会有些差异。
坐标系
由于安全性等因素的考虑,我们使用定位功能得到的坐标都不是真实的坐标,都是经过地址转换的,了解坐标系对于我们做逆地址解析有很重要的帮助,因为仅仅得到经纬度对我们用处很小,更多的时候我们需要得到语义化描述,这里我们就需要用到高德或者百度的数据源。
目前常见的坐标系有下面三种
- 地球坐标(WGS84,国际公认坐标)
- 火星坐标(GCJ02,国家标准,适用于高德百度地图大陆+港澳部分、Google地图大陆部分)
- 百度坐标(BD09,适用于百度地图大陆+港澳台部分)。
坐标系需要和地图关连才有意义,只有正确匹配地图坐标系的坐标才能在该地图上完美标识位置,否则就会存在偏移。
其他注意事项:
- IOS系统上通过定位服务CLLocation相关接口获取定位信息时,获取的经纬度坐标系是WGS84地球坐标。
- 如果使用高德或者百度IOS定位SDK中的接口,是可以直接获得火星偏移后的坐标的。
- 如果不集成集成第三方SDK,可以采取近似偏移算法:transform From WGS To GCJ。
- 如果在iOS系统地图中获取当前位置,同时在国内,那么获取到的坐标系直接是GCJ02火星坐标系。
- Android系统上通常使用高德或者百度定位SDK获取定位信息。
- 高德SDK没有坐标系参数设定,在大陆和港澳地区获取的坐标系即为GCJ02坐标系,在台湾和海外地区都是WGS84坐标系;
- 百度SDK可以自行设定坐标系参数,即返回WGS84坐标系,还是GCJ02坐标系或者BD09坐标系(注意BD09坐标系只适用于百度地图),如果设定的是GCJ02坐标系,它在大陆+港澳台地区获取的坐标系都是GCJ02坐标系。
- 海外地图(非大陆和非港澳台地区)是没有火星坐标或者百度坐标之说,都是标准的WGS84地球坐标系。
精度问题
通常手机的定位方式有如下三种:
- GPS:根据系统GPS模块获取经纬度,精度10-100米左右,限制是容易受环境影响,在室内几乎不起作用。
- 基站:根据运营商基站位置计算经纬度,精度1000-3000米左右,限制是定位较慢,精度差。
- WIFI:根据周围WIFI路由器位置计算经纬度,精度100-200米左右,限制是受周围WIFI数量和分布影响,需要打开手机WIFI开关。
切记:如果用户没有打开WIFI开关,定位的精度会受到极大的影响。
具体应用
Cordova提供官方定位插件cordova-plugin-geolocation
供开发者使用,但是由于国内网络大环境的影响,Android版无法使用,但是IOS是可以使用的,因此有大神写了基于百度定位的Android版插件。这样一来开发者需要针对版本进行区别开发,事实上由于历史原因,公司项目也是区别版本开发的。即便后来出现了基于百度定位的Android和IOS通用版本。
这里贴出封装好的代码:
(function (app) {
//调用api得到经纬度
function ios_location(success,error) {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {
console.log("(IOS)Latitude: " + position.coords.latitude + "Longitude: " + position.coords.longitude);
//注意:在得到准确位置前,需要对原生的坐标系转化成百度的坐标系
var point = new BMap.Point(position.coords.longitude, position.coords.latitude);
BMap.Convertor.translate(point, 0, function(point){
//调用百度的逆地址解析
var geoc = new BMap.Geocoder();
geoc.getLocation(point, function (rs) {
success(rs);
}, function (err) {
error({code:-1,message:'定位出错(IOS)'});
});
});
}, function (err) {
if (err.code === 1) {
error({code:-3,message:'手机定位功能未开启(IOS)'});
}else{
error({code:-1,message:'定位出错(IOS)'});
}
}, {
enableHighAccuracy: true
});
} else {
error({code:-2,message:'不支持定位(IOS)'});
}
}
//创建位置详细信息
function createDetailAddr(data) {
var addr,
pois,
addrDatas = [],
isAndroid = ionic.Platform.isAndroid();
if (isAndroid) {
addr = data.addr.substring(2);
pois = data.pois;
if (pois == null) {
addrDatas.push(addr);
} else {
var isArray = angular.isArray(pois);
if (isArray) {
if (pois.length == 0) {
addrDatas.push(addr);
} else {
for (var i = 0; i < pois.length; i++) {
if (pois[i].name) {
addrDatas.push(addr + pois[i].name);
}
}
}
} else {
if (pois.name) {
addrDatas.push(addr + pois.name);
}
}
}
} else {
addr = data.address;
pois = data.surroundingPois;
if (pois == null) {
addrDatas.push(addr);
} else {
var isArray = angular.isArray(pois);
if (isArray) {
if (pois.length == 0) {
addrDatas.push(addr);
} else {
for (var i = 0; i < pois.length; i++) {
if (pois[i].title) {
addrDatas.push(addr + pois[i].title);
}
}
}
} else {
if (pois.title) {
addrDatas.push(addr + pois.title);
}
}
}
}
return addrDatas;
}
var LocationUtil = {
startLocation: function (callback) {
// 手机环境
if (window.cordova) {
if (ionic.Platform.isAndroid()) {
baidumap_location.getCurrentPosition(function (data) {
console.log("android location success");
callback({
code:0,
message:'定位成功',
position:createDetailAddr(data)
});
}, function (err) {
console.log("android location error");
// 没有权限进入error
callback({
code:-1,
message:'手机定位功能未开启(Android)'
});
});
} else {
ios_location(function(data){
console.log("ios location success");
callback({
code:0,
message:'定位成功',
position:createDetailAddr(data)
});
},function(err){
console.log("ios location error");
callback(err);
});
}
}
}
}
app.LocationUtil = LocationUtil;
})(window);