## 前言
最近遇到一个项目需求,就是需要Android/IOS平台跳转到地图App进行导航,随即整理了这篇文章,供大家参考,也供自己做记录。
## 需要用到的方法
### 跳转APP
在APIcloud平台上,已经为我们提供了跳转APP的方法,那就是openApp方法:
//iOS中的使用方法如下:
api.openApp({
iosUrl: 'weixin://', //打开微信,其中weixin为微信的URL Scheme
appParam: {
appParam: 'app参数'
}
});
api.openApp({
iosUrl: 'app-settings:' //打开应用设置界面,支持iOS 8及以上系统
});
//Android中的使用方法如下:
api.openApp({
androidPkg: 'android.intent.action.VIEW',
mimeType: 'text/html',
uri: 'http://www.baidu.com'
}, function(ret, err) {
if (ret) {
alert(JSON.stringify(ret));
} else {
alert(JSON.stringify(err));
}
});
可见[官方文档](https://docs.apicloud.com/Client-API/api#25)
### 检测本机是否安装了指定应用
在进行APP跳转的时候,我们不确定本机是否安装了什么软件,所以我们要先检测一下本机安装了哪些地图APP,以便我们能正确的显示可用列表。那么在这里检测已安装的APP则需要用到appInstalled方法:
//异步返回结果:
api.appInstalled({
appBundle: 'xxx'
}, function(ret, err) {
if (ret.installed) {
//应用已安装
} else {
//应用未安装
}
});
//同步返回结果:
var installed = api.appInstalled({
sync: true,
appBundle: 'xxx'
});
if (installed) {
//应用已安装
} else {
//应用未安装
}
可见[官方文档](https://www.notion.so/milleros/APIcloud-APP-5dc7825a578a4225ba393e7c6f3b8784)
**但是检测本机已经安装了哪些APP并没有那么简单,特别是在IOS平台上,需要配置某些参数才能成功检测**
首先在检测前,我们需要知道被检测的appBundle;
> 在安卓平台上,appBundle就是应用包名,例如支付宝包名为com.eg.android.AlipayGphone;在IOS平台上,appBundle就是URL Scheme,一个应用只能有一个包名,但是可以有多个URL Scheme,例如支付宝在IOS上的URL Scheme就是alipays://
下面我列出一些地图APP的安卓包名和URL Scheme:
1. 高德地图 com.autonavi.minimap iosamap://
2. 百度地图 com.baidu.BaiduMap baidumap://
3. 谷歌地图 com.google.android.apps.maps comgooglemaps://
4. 腾讯地图 com.tencent.map qqmap://
5. 苹果地图 无安卓应用 [http://maps.apple.com/](http://maps.apple.com/)
**在安卓平台上我们通过包名检测是否安装某个APP是不受限制的,但是在IOS上,我们还需要额外的配置参数**
因为我使用的是APIcloud平台开发APP,所以直接在应用根目录下的res文件夹内添加**Info.plist**文件:

然后在**Info.plist**添加下列内容:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LSApplicationQueriesSchemes</key>
<array>
<!-- 高德地图 -->
<string>iosamap</string>
<!-- 百度地图 -->
<string>baidumap</string>
<!-- 谷歌地图 -->
<string>comgooglemaps</string>
<!-- 腾讯地图 -->
<string>qqmap</string>
</array>
</dict>
</plist>
如果你是IOS原生应用开发者,那么你也可以在项目目录中找到**Info.plist**文件,添加上地图参数就可以了。
在APIcloud平台上,添加完**Info.plist**文件后,还需要在根目录下的**config.xml**文件内添加下列内容:
<preference name="querySchemes" value="iosamap,baidumap,comgooglemaps,qqmap,sgmap" />
要注意是否有相同name的选项(name="querySchemes"),有则在末尾处添加上面的内容,否则直接粘贴上面那句话上去就行了。
**IOS配置好这些内容后,我们会发现检测还是没用,安装了的也返回未安装,这是因为这些配置需要在云编译后的环境内才能使用(正式版本、测试版本);**
以上内容都完成后,我们就可以进行跳转APP的操作了,在跳转APP的时候,安卓和IOS也是天差地别,下面我会娓娓道来
## 进行应用跳转
在IOS平台上我们可以直接使用URL Scheme进行跳转,但是**在Android平台上,我们不能直接使用包名进行跳转,否则虽然能跳过去,但是APP可能对我们传入的参数无任何反应**。
### 安卓平台
api.openApp({
androidPkg: 'android.intent.action.VIEW',
uri: 'bdapp://map/marker' // 打开百度地图标注
})
可以看到此处我们**通过安卓的intentVIEW来打开我们想要的应用**。
### IOS平台
api.openApp({
iosUrl: 'baidumap://map/marker', //打开百度地图标注
appParam: {
title: '一些参数'
}
})
下面也不卖关子了,直接把我写好的跳转代码贴出来
### 地图跳转参考代码
var _pageParams = {
lon: 114.062261,
lat: 22.53913,
gcj02lon: 114.108153,
gcj02lat: 22.561602,
address: '广东省深圳市福田区益田路5033号'
};
// 跳转至高德地图
function toAMap() {
// 参数文档地址:https://lbs.amap.com/api/amap-mobile/guide/android/route
var _params = {
ios: {
iosUrl: 'iosamap://viewMap',
appParam: {
sourceApplication: 'com.milleros.blog',
poiname: _pageParams.address,
lat: _pageParams.gcj02lat,
lon: _pageParams.gcj02lon,
dev: 0
}
},
android: {
androidPkg: 'android.intent.action.VIEW',
uri: 'androidamap://viewMap?lat=' + _pageParams.gcj02lat + '&lon=' + _pageParams.gcj02lon + '&poiname=' + _pageParams.address + '&dev=0&t=0'
}
};
api.openApp(_params[_systemType]);
};
// 跳转至百度地图
function toBMap() {
// 参数文档地址:https://lbsyun.baidu.com/index.php?title=uri/api/ios
var _params = {
ios: {
iosUrl: 'baidumap://map/marker',
appParam: {
src: 'com.ltkj.hkscaacpa',
coord_type: 'bd09ll',
title: _pageParams.address,
content: _pageParams.address,
location: (_pageParams.lat + ',' + _pageParams.lon)
}
},
android: {
androidPkg: 'android.intent.action.VIEW',
uri: 'bdapp://map/marker?coord_type=bd09ll&title=' + _pageParams.address + '&location=' + _pageParams.lat + ',' + _pageParams.lon + '&src=com.h2389629950.eeg'
}
};
api.openApp(_params[_systemType]);
};
// 跳转至谷歌地图
function toGMap() {
// 参数文档地址:https://developers.google.com/maps/documentation/urls/ios-urlscheme
var _params = {
ios: {
iosUrl: 'comgooglemaps://map/navi',
appParam: {
q: _pageParams.address,
center: (_pageParams.gcj02lat + ',' + _pageParams.gcj02lon)
}
},
android: {
androidPkg: 'android.intent.action.VIEW',
uri: 'https://maps.google.com/maps?q=' + _pageParams.gcj02lat + ',' + _pageParams.gcj02lon + '(' + _pageParams.address + ')'
}
};
api.openApp(_params[_systemType]);
};
// 跳转至腾讯地图
function toQQMap() {
// 参数文档地址:https://lbs.qq.com/uri_v1/guide-mobile-poiMarker.html
var _params = {
ios: {
iosUrl: 'qqmap://map/marker',
appParam: {
marker: 'coord:' + (_pageParams.gcj02lat + ',' + _pageParams.gcj02lon) + ';title:' + _pageParams.address + ';addr:' + _pageParams.address + '&referer=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77'
}
},
android: {
androidPkg: 'android.intent.action.VIEW',
uri: 'qqmap://map/marker?marker=coord:' + (_pageParams.gcj02lat + ',' + _pageParams.gcj02lon) + ';title:' + _pageParams.address + ';addr:' + _pageParams.address + '&referer=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77'
}
};
api.openApp(_params[_systemType]);
};
// 跳转至苹果地图并导航
function toAppleMap() {
// 参数文档地址:https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/MapLinks/MapLinks.html
var _params = {
ios: {
iosUrl: 'http://maps.apple.com/',
appParam: {
q: _pageParams.address
}
}
};
api.openApp(_params[_systemType]);
};
可以看到我的假数据里面还有两个gcj02开头的经纬度
var _pageParams = {
lon: 114.062261,
lat: 22.53913,
gcj02lon: 114.108153,
gcj02lat: 22.561602,
address: '广东省深圳市福田区益田路5033号'
};
是因为不同的地图提供方,使用的坐标系统可能是不同的,下面是这次涉及到的地图APP的经纬度系统:
1. 高德地图 GCJ-02
2. 百度地图 BD09
3. 谷歌地图 GCJ-02
4. 腾讯地图 GCJ-02
5. 苹果地图 GCJ-02
## 进行地图坐标系的转换
因为坐标系的不同会导致一样的经纬度数字在不同地图中表现不一致,所以这里我提供了一个网上搜集过来的转换工具(coordtransform.js)
使用方法:
//国测局坐标(火星坐标,比如高德地图在用),百度坐标,wgs84坐标(谷歌国外以及绝大部分国外在线地图使用的坐标)
//百度经纬度坐标转国测局坐标
var bd09togcj02 = coordtransform.bd09togcj02(116.404, 39.915);
//国测局坐标转百度经纬度坐标
var gcj02tobd09 = coordtransform.gcj02tobd09(116.404, 39.915);
//wgs84转国测局坐标
var wgs84togcj02 = coordtransform.wgs84togcj02(116.404, 39.915);
//国测局坐标转wgs84坐标
var gcj02towgs84 = coordtransform.gcj02towgs84(116.404, 39.915);
console.log(bd09togcj02);
console.log(gcj02tobd09);
console.log(wgs84togcj02);
console.log(gcj02towgs84);
//result
//bd09togcj02: [ 116.39762729119315, 39.90865673957631 ]
//gcj02tobd09: [ 116.41036949371029, 39.92133699351021 ]
//wgs84togcj02: [ 116.41024449916938, 39.91640428150164 ]
//gcj02towgs84: [ 116.39775550083061, 39.91359571849836 ]
coordtransform.js文件源码:
/**
* Created by Wandergis on 2015/7/8.
* 提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换
* coordtransform.js
*/
//UMD魔法代码
// if the module has no dependencies, the above pattern can be simplified to
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
} else if (typeof module === 'object' && module.exports) {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
} else {
// Browser globals (root is window)
root.coordtransform = factory();
}
}(this, function() {
//定义一些常量
var x_PI = 3.14159265358979324 * 3000.0 / 180.0;
var PI = 3.1415926535897932384626;
var a = 6378245.0;
var ee = 0.00669342162296594323;
/**
* 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换
* 即 百度 转 谷歌、高德
* @param bd_lon
* @param bd_lat
* @returns {*[]}
*/
var bd09togcj02 = function bd09togcj02(bd_lon, bd_lat) {
var bd_lon = +bd_lon;
var bd_lat = +bd_lat;
var x = bd_lon - 0.0065;
var y = bd_lat - 0.006;
var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI);
var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI);
var gg_lng = z * Math.cos(theta);
var gg_lat = z * Math.sin(theta);
return [gg_lng, gg_lat]
};
/**
* 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换
* 即谷歌、高德 转 百度
* @param lng
* @param lat
* @returns {*[]}
*/
var gcj02tobd09 = function gcj02tobd09(lng, lat) {
var lat = +lat;
var lng = +lng;
var z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI);
var theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI);
var bd_lng = z * Math.cos(theta) + 0.0065;
var bd_lat = z * Math.sin(theta) + 0.006;
return [bd_lng, bd_lat]
};
/**
* WGS84转GCj02
* @param lng
* @param lat
* @returns {*[]}
*/
var wgs84togcj02 = function wgs84togcj02(lng, lat) {
var lat = +lat;
var lng = +lng;
if (out_of_china(lng, lat)) {
return [lng, lat]
} else {
var dlat = transformlat(lng - 105.0, lat - 35.0);
var dlng = transformlng(lng - 105.0, lat - 35.0);
var radlat = lat / 180.0 * PI;
var magic = Math.sin(radlat);
magic = 1 - ee * magic * magic;
var sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
var mglat = lat + dlat;
var mglng = lng + dlng;
return [mglng, mglat]
}
};
/**
* GCJ02 转换为 WGS84
* @param lng
* @param lat
* @returns {*[]}
*/
var gcj02towgs84 = function gcj02towgs84(lng, lat) {
var lat = +lat;
var lng = +lng;
if (out_of_china(lng, lat)) {
return [lng, lat]
} else {
var dlat = transformlat(lng - 105.0, lat - 35.0);
var dlng = transformlng(lng - 105.0, lat - 35.0);
var radlat = lat / 180.0 * PI;
var magic = Math.sin(radlat);
magic = 1 - ee * magic * magic;
var sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
var mglat = lat + dlat;
var mglng = lng + dlng;
return [lng * 2 - mglng, lat * 2 - mglat]
}
};
var transformlat = function transformlat(lng, lat) {
var lat = +lat;
var lng = +lng;
var ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;
return ret
};
var transformlng = function transformlng(lng, lat) {
var lat = +lat;
var lng = +lng;
var ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0;
return ret
};
/**
* 判断是否在国内,不在国内则不做偏移
* @param lng
* @param lat
* @returns {boolean}
*/
var out_of_china = function out_of_china(lng, lat) {
var lat = +lat;
var lng = +lng;
// 纬度3.86~53.55,经度73.66~135.05
return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55);
};
return {
bd09togcj02: bd09togcj02,
gcj02tobd09: gcj02tobd09,
wgs84togcj02: wgs84togcj02,
gcj02towgs84: gcj02towgs84
}
}));
## 参考资料
1. [js 百度、高德、谷歌、火星、wgs84(2000)地图坐标相互转换的JS实现](https://blog.csdn.net/xialong_927/article/details/100208002)
2. [高德地图、腾讯地图、谷歌中国区地图与百度地图坐标系](https://www.notion.so/milleros/APIcloud-APP-5dc7825a578a4225ba393e7c6f3b8784)
3. [各种国内地图坐标系总结](https://blog.csdn.net/m0_37738114/article/details/80452485)