[HarmonyOS Next] AppGallery Connect的配置以及Map Kit的使用 原创
前言
Map Kit(地图服务)为开发者提供强大而便捷的地图能力,助力全球开发者实现个性化显示地图、位置搜索和路径规划等功能,轻松完成地图构建工作。您可以轻松地在HarmonyOS应用/元服务中集成地图相关的功能,全方位提升用户体验。
使用场景
Map Kit提供以下功能,满足绝大多数地图开发的需求:
创建地图:呈现内容包括建筑、道路、水系等。
地图交互:控制地图的交互手势和交互按钮。
在地图上绘制:添加位置标记、覆盖物以及各种形状等。
位置搜索:多种查询Poi信息的能力。
路径规划:提供驾车、步行、骑行路径规划能力。
静态图:获取一张地图图片。
地图Picker:提供地点详情展示控件、地点选取控件、区划选择控件。
通过Petal 地图应用实现导航等能力:查看位置详情、查看路径规划、发起导航、发起内容搜索。
地图计算工具:华为地图涉及的2种坐标系及其使用区域和转换。
使用
- 初始化参数
初始化mapOptions内容,mapOptions的所有参数以及分别的介绍如下文所示
名称 | 类型 | 只读 | 可选 | 说明 |
---|---|---|---|---|
mapType | MapType | 否 | 是 | 地图类型,默认值为MapType.STANDARD,异常值按默认值处理。 |
position | CameraPosition | 否 | 否 | 地图相机位置。 |
bounds | LatLngBounds | 否 | 是 | 地图展示边界,异常值根据无边界处理。(西南角纬度不能大于东北角纬度) |
minZoom | number | 否 | 是 | 地图最小图层,有效范围:[2, 20],默认值为2,异常值按默认值处理。如果设置的最小缩放级别小于2,minZoom会取2。 |
maxZoom | number | 否 | 是 | 地图最大图层,有效范围:[2, 20],默认值为20,异常值按默认值处理。如果设置的最大缩放级别大于20,maxZoom会取20。 |
rotateGesturesEnabled | boolean | 否 | 是 | 是否支持旋转手势,默认值为true,异常值按默认值处理。- true:支持 - false:不支持 |
scrollGesturesEnabled | boolean | 否 | 是 | 是否支持滑动手势,默认值为true,异常值按默认值处理。- true:支持 - false:不支持 |
zoomGesturesEnabled | boolean | 否 | 是 | 是否支持缩放手势,默认值为true,异常值按默认值处理。- true:支持 - false:不支持 |
tiltGesturesEnabled | boolean | 否 | 是 | 是否支持倾斜手势,默认值为true,异常值按默认值处理。- true:支持 - false:不支持 |
zoomControlsEnabled | boolean | 否 | 是 | 是否展示缩放控件,默认值为true,异常值按默认值处理。- true:展示 - false:不展示 |
myLocationControlsEnabled | boolean | 否 | 是 | 是否展示定位按钮,默认值为false,异常值按默认值处理。- true:展示 - false:不展示 |
compassControlsEnabled | boolean | 否 | 是 | 是否展示指南针控件,默认值为true,异常值按默认值处理。- true:展示 - false:不展示 |
scaleControlsEnabled | boolean | 否 | 是 | 是否展示比例尺,默认值为false,异常值按默认值处理。- true:展示 - false:不展示 |
padding | Padding | 否 | 是 | 设置地图和边界的距离,默认值为{ left: 0 , top: 0 , right: 0 , bottom: 0 }。 |
styleId | string | 否 | 是 | 自定义样式ID。使用方式详见自定义地图样式章节。 |
dayNightMode | DayNightMode | 否 | 是 | 日间夜间模式,默认值为DayNightMode.DAY(日间模式)。 |
alwaysShowScaleEnabled | boolean | 否 | 是 | 是否一直显示比例尺,只有比例尺启时该参数才生效。启用比例尺可以由地图初始化时scaleControlsEnabled属性设置为true或者通过setScaleControlsEnabled方法设置为true。- true:始终显示 - false:关闭始终显示,默认是false。 |
-
初始化回调函数
初始化回调的时候,我们可以对地图进行操作
比如添加折线,添加标记等 -
除此之外,map kit还提供了很多的函数可以让我们调用
内容详解
1.权限申请
我们如果想要申请Map Kit的权限,需要在AGC中申请调试证书、注册调试设备、申请调试Profile后,再手动配置签名信息。(自动签名我试过好多次都失败了)
需要的签名证书分别由四个,分别为
- 密钥:格式为.p12,包含非对称加密中使用的公钥和私钥,存储在密钥库文件中,公钥和私钥对用于数字签名和验证。
- 证书请求文件:格式为.csr,全称为Certificate Signing Request,包含密钥对中的公钥和公共名称、组织名称、组织单位等信息,用于向AppGallery Connect申请数字证书。
- 数字证书:格式为.cer,由华为AppGallery Connect颁发。
- Profile文件:格式为.p7b,包含HarmonyOS应用/元服务的包名、数字证书信息、描述应用/元服务允许申请的证书权限列表,以及允许应用/元服务调试的设备列表(如果应用/元服务类型为Release类型,则设备列表为空)等内容,每个应用/元服务包中均必须包含一个Profile文件。
看不明白没关系,我们一步步来进行申请
1.生成密钥和证书请求文件
我们点击new![image.png]进行生成操作
输入我们的密码,点击ok,完成创建
这里我们定义以下我们的名称,这个要记住,后续进行签名的时候需要用得到
点击next,创建完p12文件,接下来就需要我们创建csr文件,步骤如下所示
这下我们就创建好我们的应用了,接下来就需要申请数字证书和Profile文件了
2.生成数字证书
进入我们的AppGallery Connect(简称AGC),我们下面需要在上面拿到我们需要的另外两个文件
进入证书、APP ID和Profile
按照下图进入证书,点击新增证书,填好我们的内容,以及选择我们刚刚生成的文件,就可以新增对应的数字证书
我们新建完之后就可以点击下载来下载我们的数字证书
接着我们需要将下载的这个文件保存在我们的固定目录,方便之后使用
3.创建项目与应用
进入项目页面
点击新建项目
填好信息后一直下一步到这个页面,我们要配置默认数据处理位置
选择中国
然后我们的项目就创建好了,现在返回到我们的证书、APP ID和Profile页面,我们需要在APP ID里面创建我们的应用
我们将应用信息填好,应用包名为我们对应项目的应用包名,可以在项目中查看
选择好我们刚刚创建的项目
点击下一步后,选择我们要开启的权限,这里选择地图服务
然后我们的应用就创建好了,最后就剩下我们的最后一步
4.创建Profile文件
下面我们需要添加我们的设备,我们需要知道我们设备的UDID号,方法如下方所示
使用PC连接手机后,打开命令行工具,进入HDC目录(一般为:DevEco Studio安装目录/sdk/[SDK版本]/openharmony/toolchains),输入hdc shell bm get --udid命令,获取设备的UDID。
这样我们就可以找到自己的UDID,我们添加设备后,输入刚刚的信息
然后我们进入Profile页面,添加Profile,项目,证书和设备选择刚刚添加的
这样我们的Profile就生成好了,下载我们文件
和刚刚一样,保存在我们固定的地方
5.最后的配置
我们生成好我们需要的几个文件后,再最后再进行一个配置
打开我们的项目结构
填好刚刚生成的签名信息
点击我们右边的这个小指纹
我们将生成的这个指纹复制下来
返回我的项目,将刚刚复制到的内容粘贴到这个公钥指纹里面保存
记录下来这个Client ID
在我们的module.json5中配置以下代码
"metadata": [
{
"name": "client_id",
"value": "112822483" // 配置为获取的Client ID
}
]
这样我们的权限就完全申请好了,如果还是没法用,可能时因为配置好后需要等待十分钟
2.创建地图
我们先创建mapOptions对象,进行初始化后绑定回调就可以显示我们的页面
初始化mapOptions
private mapOptions?: mapCommon.MapOptions;
this.mapOptions = {
position: {
target: {
//设置经纬度
latitude: 39.9,
longitude: 116.4
},
zoom: 10
}
};
设置好回调函数
private callback?: AsyncCallback<map.MapComponentController>;
// 地图初始化的回调
this.callback = async (err, mapController) => {
if (!err) {
// 获取地图的控制器类,用来操作地图
this.mapController = mapController;
this.mapEventManager = this.mapController.getEventManager();
let callback = () => {
console.info(this.TAG, `on-mapLoad`);
}
this.mapEventManager.on("mapLoad", callback);
}
};
绑定
build() {
Stack() {
// 调用MapComponent组件初始化地图
MapComponent({ mapOptions: this.mapOptions, mapCallback: this.callback }).width('100%').height('100%');
}.height('100%')
}
这样我们一个简单的显示就这样写好了
完整示例:
import { MapComponent, mapCommon, map } from '@kit.MapKit';
import { AsyncCallback } from '@kit.BasicServicesKit';
@Entry
@Component
struct HuaweiMapDemo {
private TAG = "HuaweiMapDemo";
private mapOptions?: mapCommon.MapOptions;
private callback?: AsyncCallback<map.MapComponentController>;
private mapController?: map.MapComponentController;
private mapEventManager?: map.MapEventManager;
aboutToAppear(): void {
// 地图初始化参数,设置地图中心点坐标及层级
this.mapOptions = {
position: {
target: {
latitude: 39.9,
longitude: 116.4
},
zoom: 10
}
};
// 地图初始化的回调
this.callback = async (err, mapController) => {
if (!err) {
// 获取地图的控制器类,用来操作地图
this.mapController = mapController;
this.mapEventManager = this.mapController.getEventManager();
let callback = () => {
console.info(this.TAG, `on-mapLoad`);
}
this.mapEventManager.on("mapLoad", callback);
}
};
}
// 页面每次显示时触发一次,包括路由过程、应用进入前台等场景,仅@Entry装饰的自定义组件生效
onPageShow(): void {
// 将地图切换到前台
if (this.mapController !== undefined) {
this.mapController.show();
}
}
// 页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景,仅@Entry装饰的自定义组件生效。
onPageHide(): void {
// 将地图切换到后台
if (this.mapController !== undefined) {
this.mapController.hide();
}
}
build() {
Stack() {
// 调用MapComponent组件初始化地图
MapComponent({ mapOptions: this.mapOptions, mapCallback: this.callback }).width('100%').height('100%');
}.height('100%')
}
}
3.我的位置
查询我的位置有两种方法,一种是使用LocationButton组件获取我们的经纬度,较为推荐,第二种是通过ohos.permission.LOCATION权限申请
第二种方法,可以看我的这一期内容,里面有如何申请权限以及使用方法
LOCATION权限申请
这里我使用第二种方法,我们动态申请权限的弹窗我稍微修改了一下,
//申请权限
applyPermission(context: Context,
permissions: Array<Permissions>): Promise<boolean> {
let atManager = abilityAccessCtrl.createAtManager();
return new Promise((resolve: (res: boolean) => void, reject: (e: ESObject) => void) => {
atManager.requestPermissionsFromUser(context, permissions).then((data) => {
let grantStatus: Array<number> = data.authResults;
resolve(grantStatus.every(item => item === 0))
}).catch((err: ESObject) => {
reject(err)
})
})
}
我们如果想要显示启动我的位置的按钮,需要加入这两句
以下2句启用需在权限申请完后调用,否则首次定位功能不生效
// 启用我的位置图层
this.mapController.setMyLocationEnabled(true);
// 启用我的位置按钮
this.mapController.setMyLocationControlsEnabled(true)
样例代码:
import { MapComponent, mapCommon, map } from '@kit.MapKit';
import { AsyncCallback } from '@kit.BasicServicesKit';
import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
@Entry
@Component
struct Index {
private mapOption?: mapCommon.MapOptions; //地图配置
private callback?: AsyncCallback<map.MapComponentController>; //初始化完成回调
private mapController?: map.MapComponentController; //地图控制器
aboutToAppear(): void {
// 地图初始化参数,设置地图中心点坐标及层级
this.mapOption = {
position: {
target: {
latitude: 39.9,
longitude: 116.4
},
zoom: 10 //缩放级别
}
};
// 地图初始化的回调
this.callback = async (err, mapController) => {
if (!err) {
// 获取地图的控制器类,用来操作地图
this.mapController = mapController;
//申请权限
let list:Array<Permissions>=['ohos.permission.APPROXIMATELY_LOCATION','ohos.permission.LOCATION']
try {
await this.applyPermission(getContext(this), list)
// 启用我的位置图层
this.mapController.setMyLocationEnabled(true);
// 启用我的位置按钮
this.mapController.setMyLocationControlsEnabled(true)
}
catch (e){
}
}
};
}
//申请权限
applyPermission(context: Context,
permissions: Array<Permissions>): Promise<boolean> {
let atManager = abilityAccessCtrl.createAtManager();
return new Promise((resolve: (res: boolean) => void, reject: (e: ESObject) => void) => {
atManager.requestPermissionsFromUser(context, permissions).then((data) => {
let grantStatus: Array<number> = data.authResults;
resolve(grantStatus.every(item => item === 0))
}).catch((err: ESObject) => {
reject(err)
})
})
}
build() {
Stack() {
// 调用MapComponent组件初始化地图
MapComponent({ mapOptions: this.mapOption, mapCallback: this.callback }).width('100%').height('100%');
}.height('100%')
}
}
4.地图交互
标点:
我们标点使用mapController.addMarker(markerOptions);这个api
默认图标:
let markerOptions: mapCommon.MarkerOptions = {
position: {//标点经纬度
latitude: 39.9,
longitude: 116.4
},
// rotation: 0,//标记旋转角度
// alpha: 1,//标记透明度
// clickable: true,//是否可点击
// draggable: true, //是否可拖拽
// icon:'xxxxx'//自定义图标,不设显示默认图标
};
// 创建Marker
this.marker = await this.mapController.addMarker(markerOptions);
5.其他功能
1.位置搜索
mapkit还提供了关键字搜索和周边搜索等,我们可以根据这个来进行准确的查找地点
关键字搜索:SearchByTextResult
site.SearchByTextParams为关键字搜索的参数,我们设置好这些参数之后,便可以调用site.searchByText函数来进行搜索
let params: site.SearchByTextParams = {
// 指定关键字
query: "Piazzale Dante, 41, 55049 Viareggio, Tuscany, Italy",
// 经纬度坐标
location: {
latitude: 31.984,
longitude: 118.76625
},
// 指定地理位置的范围半径
radius: 10000,
language: "en"
};
// 返回关键字搜索结果
const result = await site.searchByText(params);
console.info("Succeeded in searching by text.");
周边搜索:NearbySearchResult
site.NearbySearchParams 为关键字搜索的参数,我们设置好这些参数之后,便可以调用site.nearbySearch函数来进行搜索
let params: site.NearbySearchParams = {
// 指定关键字
query: "stazione di pomezia",
// 经纬度坐标
location: {
latitude: 31.984410259206815,
longitude: 118.76625379397866
},
// 指定地理位置的范围半径
radius: 5000,
// 指定需要展示的poi类别
poiTypes: ["NATIONAL_RAILWAY_STATION"],
language: "en",
pageIndex: 1,
pageSize: 1
};
// 返回周边搜索结果
const result = await site.nearbySearch(params);
console.info("Succeeded in searching nearby.");
2.路线规划
mapkit还提供了navi 来进行路线规划
驾车路径规划
根据起终点坐标检索符合条件的驾车路径规划方案。支持以下功能:
- 支持一次请求返回多条路线,最多支持3条路线。
- 最多支持5个途经点。
- 支持未来出行规划。
- 支持根据实时路况进行合理路线规划。
- 支持多种路线偏好选择,如时间最短、避免经过收费的公路、避开高速公路、距离优先等。
async testDrivingRoutes(){
let params: navi.DrivingRouteParams = {
// 起点的经纬度
origins: [{
"latitude": 31.982129213545843,
"longitude": 120.27745557768591
}],
// 终点的经纬度
destination: {
"latitude": 31.982129213545843,
"longitude": 120.27745557768591
},
// 路径的途经点
waypoints: [{ "latitude": 31.967236140819114, "longitude": 120.27142088866847 },
{ "latitude": 31.972868002238872, "longitude": 120.2943211817165 },
{ "latitude": 31.98469327973332, "longitude": 120.29101107384068 }],
language: "zh_CN"
};
const result = await navi.getDrivingRoutes(params);
console.info("Succeeded in getting driving routes.");
}
步行路径规划
根据起终点坐标检索符合条件的步行路径规划方案。支持以下功能:
- 支持150km以内的步行路径规划能力。
- 融入出行策略(时间最短、避免轮渡)。
async testWalkingRoutes(){
let params: navi.RouteParams = {
// 起点的经纬度
origins: [{ "latitude": 39.992281, "longitude": 116.31088 }, { "latitude": 39.996, "longitude": 116.311 }],
// 终点的经纬度
destination: { "latitude": 39.94, "longitude": 116.311 },
language: "zh_CN"
};
const result = await navi.getWalkingRoutes(params);
console.info("Succeeded in getting walking routes.");
}
骑行路径规划
根据起终点坐标检索符合条件的骑行路径规划方案。支持以下功能:
- 支持500km以内的骑行路径规划能力。
- 融入出行策略(时间最短、避免轮渡)。
async testCyclingRoutes() {
let params: navi.RouteParams = {
// 起点的经纬度
origins: [{ latitude: 31.9844102, longitude: 118.7662537 }],
// 终点的经纬度
destination: { latitude: 31.9874102, longitude: 118.7362537 },
language: "zh_CN"
};
const result = await navi.getCyclingRoutes(params);
console.info("Succeeded in getting cycling routes.");
}
非常棒的内容,华为现在有内容创作激励,老师可以参与