鸿蒙6.0地图导航应用(可集成到其他APP)开发方案

一、前言

随着移动互联网的深入发展,地图导航功能已成为现代应用不可或缺的基础能力。无论是出行服务、本地生活还是企业级应用,集成地图导航都能大幅提升用户体验和应用价值。鸿蒙6.0推出的Map Kit提供了完整的地图服务解决方案,支持嵌入式地图渲染、调起Petal Maps导航、POI搜索等核心功能,更关键的是其设计充分考虑了模块化和可复用性,便于开发者将地图导航能力封装为独立组件,灵活集成到各类应用中。

本文将从开发环境配置、核心功能实现讲到最核心的模块化设计 ,手把手教你打造一个可插拔的地图导航组件,实现"一行代码集成"的极致开发体验。

二、开发环境准备

2.1 必要工具安装

  • DevEco Studio 5.0+:支持HarmonyOS NEXT 6.0开发的官方IDE
  • HarmonyOS SDK:确保包含Map Kit、Location Kit等必要组件
  • Node.js 18+:部分构建依赖需要

2.2 AGC应用创建与地图服务开通

  1. 登录华为AppGallery Connect
  2. 创建应用,选择"手机"设备类型,勾选"HarmonyOS"
  3. 在"增长服务 > 地图服务"中开通Map Kit
  4. 创建API Key并记录(后续配置使用)

2.3 项目结构创建

推荐采用多模块架构设计:

复制代码
MyMapNavigationKit/
├── entry/                    # 主应用(集成示例)
├── mapnavigation/           # 地图导航模块(HAR包)
│   └── src/main/
│       ├── ets/
│       │   ├── components/  # 可复用组件
│       │   ├── services/    # 服务封装
│       │   └── pages/       # 导航页面
│       └── module.json5
└── mapnavigationconsumer/   # 消费示例APP

三、权限声明与配置

3.1 权限清单

地图导航功能需要以下权限,在module.json5中声明:

json 复制代码
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.LOCATION",
        "reason": "$string:location_reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "always"
        }
      },
      {
        "name": "ohos.permission.APPROXIMATELY_LOCATION",
        "reason": "$string:location_reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "always"
        }
      },
      {
        "name": "ohos.permission.INTERNET",
        "reason": "$string:network_reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "always"
        }
      }
    ]
  }
}

3.2 权限申请文案(en_US.json)

json 复制代码
{
  "string": {
    "location_reason": "地图导航功能需要获取您的位置信息以提供精准定位和导航服务",
    "network_reason": "地图数据加载需要网络连接"
  }
}

四、嵌入式地图核心开发

4.1 地图初始化与渲染

MapComponent是鸿蒙6.0提供的原生地图渲染组件,支持在应用内直接展示地图:

typescript 复制代码
// mapnavigation/src/main/ets/pages/MapView.ets
import { MapComponent, mapCommon, map } from '@kit.MapKit'
import { AsyncCallback } from '@kit.BasicServicesKit'

@Component
export struct MapView {
  // 地图控制器
  private mapController?: map.MapComponentController
  // 地图配置
  private mapOptions: mapCommon.MapOptions = {
    position: {
      target: { latitude: 39.9042, longitude: 116.4074 }, // 北京天安门
      zoom: 12
    },
    mapType: mapCommon.MapType.STANDARD
  }
  // 回调函数
  private mapCallback: AsyncCallback<map.MapComponentController> = 
    (err: Error, controller: map.MapComponentController) => {
    if (!err && controller) {
      this.mapController = controller
      // 启用我的位置
      this.mapController.setMyLocationEnabled(true)
      // 设置地图监听
      this.setupMapListeners()
    }
  }

  // 地图事件监听
  private setupMapListeners(): void {
    if (this.mapController) {
      // 点击事件
      this.mapController.on('click', (event: mapCommon.MapClickEvent) => {
        console.info(`地图点击: 经度${event.longitude}, 纬度${event.latitude}`)
      })
      
      // 拖拽事件
      this.mapController.on('cameraIdle', (event: mapCommon.CameraIdleEvent) => {
        console.info(`相机位置变更: 缩放级别${event.zoom}`)
      })
    }
  }

  aboutToDisappear(): void {
    // 清理监听
    if (this.mapController) {
      this.mapController.off('click')
      this.mapController.off('cameraIdle')
    }
  }

  build() {
    Stack() {
      MapComponent({
        mapOptions: this.mapOptions,
        mapCallback: this.mapCallback
      })
      .width('100%')
      .height('100%')
      
      // 自定义覆盖层
      this.buildOverlayControls()
    }
  }

  @Builder
  buildOverlayControls() {
    Column() {
      // 缩放控制按钮
      Row() {
        Button('+')
          .onClick(() => this.mapController?.setZoom(1))
        Button('-')
          .onClick(() => this.mapController?.setZoom(-1))
      }
    }
    .alignItems(HorizontalAlign.End)
    .padding(16)
  }
}

4.2 地图类型切换(鸿蒙6.0.0+新特性)

鸿蒙6.0.0(20)版本新增了卫星图、混合地图、地形图等丰富地图类型:

typescript 复制代码
// 地图类型枚举
enum MapDisplayType {
  STANDARD = 0,     // 标准地图
  SATELLITE = 1,    // 卫星地图
  HYBRID = 2,       // 混合地图
  TERRAIN = 3       // 地形图
}

// 切换地图类型方法
setMapDisplayType(type: MapDisplayType): void {
  if (this.mapController) {
    // 根据类型设置不同的mapType
    switch (type) {
      case MapDisplayType.SATELLITE:
        this.mapController.setMapType(mapCommon.MapType.SATELLITE)
        break
      case MapDisplayType.HYBRID:
        this.mapController.setMapType(mapCommon.MapType.HYBRID)
        break
      case MapDisplayType.TERRAIN:
        this.mapController.setMapType(mapCommon.MapType.TERRAIN)
        break
      default:
        this.mapController.setMapType(mapCommon.MapType.STANDARD)
    }
  }
}

4.3 标记点与路线绘制

typescript 复制代码
// 添加标记点
addMarker(lat: number, lng: number, title: string): string {
  if (!this.mapController) return ''
  
  const markerOptions: mapCommon.MarkerOptions = {
    position: { latitude: lat, longitude: lng },
    title: title,
    clickable: true,
    anchorU: 0.5,
    anchorV: 1.0
  }
  
  return this.mapController.addMarker(markerOptions)
}

// 绘制导航路线
drawRoute(startLat: number, startLng: number, endLat: number, endLng: number): void {
  if (!this.mapController) return
  
  // 定义路线节点
  const routeNodes: mapCommon.LatLng[] = [
    { latitude: startLat, longitude: startLng },
    { latitude: endLat, longitude: endLng }
  ]
  
  // 绘制折线
  const polylineOptions: mapCommon.PolylineOptions = {
    points: routeNodes,
    strokeColor: Color.Blue,
    strokeWidth: 8,
    arrowColor: Color.White,
    arrowEnabled: true
  }
  
  this.mapController.addPolyline(polylineOptions)
}

五、导航功能实现

5.1 调起Petal Maps进行导航

使用petalMaps命名空间调起系统地图应用:

typescript 复制代码
// mapnavigation/src/main/ets/services/NavigationService.ets
import { petalMaps } from '@kit.MapKit'
import { common } from '@kit.AbilityKit'
import { hilog } from '@kit.PerformanceAnalysisKit'

const TAG = 'NavigationService'
const DOMAIN = 0x0001

export class NavigationService {
  private context: common.UIAbilityContext

  constructor(context: common.UIAbilityContext) {
    this.context = context
  }

  /**
   * 发起导航
   * @param destLat 目的地纬度
   * @param destLng 目的地经度
   * @param destName 目的地名称
   */
  async startNavigation(destLat: number, destLng: number, destName: string): Promise<void> {
    try {
      const naviParams: petalMaps.NaviParams = {
        destinationPosition: {
          latitude: destLat,
          longitude: destLng,
          name: destName
        },
        navigationMode: petalMaps.NavigationMode.COLLECT
      }
      
      await petalMaps.openMapNavi(this.context, naviParams)
      hilog.info(DOMAIN, TAG, '导航发起成功')
    } catch (error) {
      hilog.error(DOMAIN, TAG, `导航发起失败: ${JSON.stringify(error)}`)
      throw error
    }
  }

  /**
   * 路线规划
   * @param destLat 目的地纬度
   * @param destLng 目的地经度
   */
  async planRoute(destLat: number, destLng: number): Promise<void> {
    try {
      const routeParams: petalMaps.RoutePlanParams = {
        destinationPosition: {
          latitude: destLat,
          longitude: destLng
        },
        // 途经点(可选)
        waypoints: [],
        // 偏好路线:0-推荐, 1-高速优先, 2-少收费, 3-避堵
        preference: 0
      }
      
      await petalMaps.openMapRoutePlan(this.context, routeParams)
      hilog.info(DOMAIN, TAG, '路线规划成功')
    } catch (error) {
      hilog.error(DOMAIN, TAG, `路线规划失败: ${JSON.stringify(error)}`)
      throw error
    }
  }

  /**
   * POI搜索
   * @param keyword 搜索关键词
   */
  async searchPOI(keyword: string): Promise<void> {
    try {
      const searchParams: petalMaps.TextSearchParams = {
        searchWord: keyword
      }
      
      await petalMaps.openMapTextSearch(this.context, searchParams)
      hilog.info(DOMAIN, TAG, 'POI搜索成功')
    } catch (error) {
      hilog.error(DOMAIN, TAG, `POI搜索失败: ${JSON.stringify(error)}`)
      throw error
    }
  }

  /**
   * 查看POI详情
   * @param poiId POI ID
   */
  async showPOIDetail(poiId: string): Promise<void> {
    try {
      await petalMaps.openMapPoiDetail(this.context, { poiId: poiId })
      hilog.info(DOMAIN, TAG, 'POI详情打开成功')
    } catch (error) {
      hilog.error(DOMAIN, TAG, `POI详情打开失败: ${JSON.stringify(error)}`)
      throw error
    }
  }
}

5.2 坐标系统注意事项

typescript 复制代码
// 坐标转换工具
export class CoordinateConverter {
  /**
   * GCJ02转WGS84(中国大陆以外)
   * 中国大陆使用GCJ02坐标系
   */
  static gcj02ToWgs84(lat: number, lng: number): { latitude: number, longitude: number } {
    // 简化转换,实际生产建议使用专业坐标转换库
    const PI = 3.1415926535897932384626
    const a = 6378245.0
    const ee = 0.00669342162296594323
    
    let dLat = this.transformLat(lng - 105.0, lat - 35.0)
    let dLng = this.transformLng(lng - 105.0, lat - 35.0)
    
    const radLat = lat / 180.0 * PI
    let magic = Math.sin(radLat)
    magic = 1 - ee * magic * magic
    const sqrtMagic = Math.sqrt(magic)
    
    dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI)
    dLng = (dLng * 180.0) / (a / sqrtMagic * Math.cos(radLat) * PI)
    
    return {
      latitude: lat - dLat,
      longitude: lng - dLng
    }
  }
  
  private static transformLat(x: number, y: number): number {
    const PI = 3.1415926535897932384626
    let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y
    ret += 0.2 * Math.sqrt(Math.abs(x))
    ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0
    ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0
    ret += (160.0 * Math.sin(y / 12.0 * PI) + 320 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0
    return ret
  }
  
  private static transformLng(x: number, y: number): number {
    const PI = 3.1415926535897932384626
    let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x))
    ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0
    ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0
    ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0
    return ret
  }
}

六、模块化设计:打造可集成导航组件

这是本文的核心章节,我们将地图导航能力封装为独立的共享库(SharedLibrary/HAR),实现应用间的复用。

6.1 整体架构设计

typescript 复制代码
// mapnavigation/src/main/ets/NavigationKit.ets

/**
 * 导航套件 - 统一入口
 * 封装地图导航相关能力,提供简洁的API给宿主应用
 */

// ==================== 类型定义 ====================

/** 目的地信息 */
export interface Destination {
  latitude: number
  longitude: number
  name?: string
  address?: string
}

/** 导航配置 */
export interface NavigationConfig {
  /** 初始中心点 */
  center?: Destination
  /** 默认缩放级别 */
  zoom?: number
  /** 地图类型: standard | satellite | hybrid | terrain */
  mapType?: 'standard' | 'satellite' | 'hybrid' | 'terrain'
  /** 是否显示我的位置 */
  showMyLocation?: boolean
  /** 是否启用路线规划 */
  enableRoutePlan?: boolean
  /** 导航回调 */
  onNavigationStart?: (dest: Destination) => void
  onNavigationEnd?: (dest: Destination) => void
  onError?: (error: Error) => void
  onMapClick?: (lat: number, lng: number) => void
}

/** 主题配置 */
export interface NavigationTheme {
  primaryColor?: string
  secondaryColor?: string
  backgroundColor?: string
  textColor?: string
  markerColor?: string
}

/** 导航事件 */
export interface NavigationEvent {
  type: 'click' | 'longPress' | 'routeSelected' | 'navigationStart' | 'navigationEnd'
  data?: any
}

// ==================== 核心类 ====================

import { common } from '@kit.AbilityKit'
import { MapView } from './components/MapView'
import { NavigationService } from './services/NavigationService'

/**
 * 导航套件主类
 * 提供嵌入式地图和调起导航两种模式
 */
export class NavigationKit {
  private context: common.UIAbilityContext
  private config: NavigationConfig
  private navigationService: NavigationService
  private currentTheme: NavigationTheme

  constructor(context: common.UIAbilityContext, config?: NavigationConfig) {
    this.context = context
    this.config = {
      zoom: 12,
      mapType: 'standard',
      showMyLocation: true,
      enableRoutePlan: true,
      ...config
    }
    this.navigationService = new NavigationService(context)
    this.currentTheme = {}
  }

  /**
   * 发起导航(调起Petal Maps)
   * @param destination 目的地
   */
  async startNavigation(destination: Destination): Promise<void> {
    try {
      this.config.onNavigationStart?.(destination)
      await this.navigationService.startNavigation(
        destination.latitude,
        destination.longitude,
        destination.name || '目的地'
      )
    } catch (error) {
      this.config.onError?.(error as Error)
      throw error
    }
  }

  /**
   * 路线规划
   * @param destination 目的地
   */
  async planRoute(destination: Destination): Promise<void> {
    await this.navigationService.planRoute(
      destination.latitude,
      destination.longitude
    )
  }

  /**
   * 创建嵌入式地图组件(供UI调用)
   */
  createMapComponent(): MapView {
    return new MapView(this.config, this.navigationService)
  }

  /**
   * 设置主题
   * @param theme 主题配置
   */
  setTheme(theme: NavigationTheme): void {
    this.currentTheme = theme
    // 主题应用到全局样式
    // 实际实现可使用AppStorage或事件总线
  }

  /**
   * 注册事件监听
   * @param callback 事件回调
   */
  onEvent(callback: (event: NavigationEvent) => void): void {
    // 事件总线实现
    EventBus.getInstance().subscribe(callback)
  }

  /**
   * 销毁资源
   */
  destroy(): void {
    EventBus.getInstance().unsubscribe()
  }
}

// ==================== 事件总线(简化实现) ====================

class EventBusClass {
  private subscribers: Array<(event: NavigationEvent) => void> = []

  subscribe(callback: (event: NavigationEvent) => void): void {
    this.subscribers.push(callback)
  }

  unsubscribe(callback?: (event: NavigationEvent) => void): void {
    if (callback) {
      this.subscribers = this.subscribers.filter(cb => cb !== callback)
    } else {
      this.subscribers = []
    }
  }

  publish(event: NavigationEvent): void {
    this.subscribers.forEach(cb => cb(event))
  }

  static getInstance(): EventBusClass {
    if (!EventBusClass.instance) {
      EventBusClass.instance = new EventBusClass()
    }
    return EventBusClass.instance
  }

  private static instance: EventBusClass
}

// 导出单例
const EventBus = EventBusClass

// ==================== 工厂函数 ====================

/**
 * 创建导航套件实例
 * 宿主应用使用此函数快速初始化
 */
export function createNavigationKit(
  context: common.UIAbilityContext,
  config?: NavigationConfig
): NavigationKit {
  return new NavigationKit(context, config)
}

6.3 宿主APP集成示例

方式一:调起Petal Maps导航(推荐,最简集成)
typescript 复制代码
// hostapp/src/main/ets/pages/Index.ets
import { createNavigationKit, Destination } from '../library/mapnavigation'

@Entry
@Component
struct Index {
  private navKit = createNavigationKit(getContext(this) as common.UIAbilityContext, {
    onNavigationStart: (dest) => {
      console.info(`开始导航到: ${dest.name}`)
    },
    onNavigationEnd: (dest) => {
      console.info(`导航结束: ${dest.name}`)
    },
    onError: (error) => {
      console.error(`导航错误: ${error.message}`)
    }
  })

  async aboutToAppear(): void {
    // 模拟:从服务器获取目的地
    const destination: Destination = {
      latitude: 31.230416,
      longitude: 121.473701,
      name: '上海外滩',
      address: '上海市黄浦区中山东一路'
    }

    // 一行代码发起导航!
    await this.navKit.startNavigation(destination)
  }

  build() {
    Column() {
      Text('地图导航示例')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
      
      Button('导航到上海外滩')
        .onClick(async () => {
          await this.navKit.startNavigation({
            latitude: 31.230416,
            longitude: 121.473701,
            name: '上海外滩'
          })
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}
方式二:嵌入式地图集成
typescript 复制代码
// hostapp/src/main/ets/pages/MapIntegration.ets
import { createNavigationKit, NavigationConfig } from '../library/mapnavigation'
import { MapView } from '../library/mapnavigation/components/MapView'

@Entry
@Component
struct MapIntegration {
  private navKit: any
  private mapView: MapView | null = null

  aboutToAppear(): void {
    const config: NavigationConfig = {
      center: { latitude: 39.9042, longitude: 116.4074 },
      zoom: 12,
      mapType: 'standard',
      showMyLocation: true,
      onMapClick: (lat, lng) => {
        console.info(`点击地图位置: ${lat}, ${lng}`)
        // 在点击位置添加标记
        this.mapView?.addMarker(lat, lng, '新标记')
      }
    }

    this.navKit = createNavigationKit(getContext(this) as common.UIAbilityContext, config)
    this.mapView = this.navKit.createMapComponent()
  }

  build() {
    Stack() {
      // 嵌入式地图
      if (this.mapView) {
        this.mapView
      }

      // 顶部工具栏
      Column() {
        Row() {
          Button('标准地图')
            .onClick(() => this.mapView?.setMapDisplayType(0))
          
          Button('卫星图')
            .onClick(() => this.mapView?.setMapDisplayType(1))
          
          Button('混合地图')
            .onClick(() => this.mapView?.setMapDisplayType(2))
        }
        .padding(10)
        .backgroundColor('#80000000')
        .borderRadius(8)
      }
      .width('100%')
      .padding(16)
      .alignItems(HorizontalAlign.Start)

      // 底部导航栏
      Column() {
        Row() {
          Button('发起导航')
            .onClick(async () => {
              await this.navKit.startNavigation({
                latitude: 31.230416,
                longitude: 121.473701,
                name: '上海外滩'
              })
            })
          
          Button('路线规划')
            .onClick(async () => {
              await this.navKit.planRoute({
                latitude: 31.230416,
                longitude: 121.473701
              })
            })
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceEvenly)
      }
      .width('100%')
      .padding(16)
      .backgroundColor(Color.White)
    }
  }

  aboutToDisappear(): void {
    this.navKit?.destroy()
  }
}

6.4 高级配置:自定义主题与回调

typescript 复制代码
// 完整配置示例
const advancedConfig: NavigationConfig = {
  // 地图初始配置
  center: {
    latitude: 39.9042,
    longitude: 116.4074,
    name: '北京市'
  },
  zoom: 14,
  mapType: 'standard',
  showMyLocation: true,
  enableRoutePlan: true,

  // 事件回调
  onNavigationStart: (dest) => {
    // 统计上报
    console.info(`[埋点] 开始导航 to ${dest.name}`)
    // 跳转页面
    // 显示loading
  },

  onNavigationEnd: (dest) => {
    console.info(`[埋点] 导航完成 from ${dest.name}`)
    // 评价弹窗
    // 结束计时
  },

  onError: (error) => {
    console.error(`[异常] ${error.code}: ${error.message}`)
    // 错误上报
    // 用户提示
    promptAction.showToast({ message: '导航启动失败,请重试' })
  },

  onMapClick: (lat, lng) => {
    // 反向地理编码获取地址
    // 更新UI显示地址
  }
}

// 主题配置
const darkTheme: NavigationTheme = {
  primaryColor: '#1F89FF',
  secondaryColor: '#409CFF',
  backgroundColor: '#1A1A1A',
  textColor: '#FFFFFF',
  markerColor: '#FF6B6B'
}

// 应用主题
navKit.setTheme(darkTheme)

七、完整项目结构与配置

7.1 mapnavigation模块的oh-package.json5

json 复制代码
{
  "name": "mapnavigation",
  "version": "1.0.0",
  "description": "鸿蒙地图导航可集成组件",
  "main": "",
  "type": "module",
  "dependencies": {
    "@kit.MapKit": "5.0.0+",
    "@kit.BasicServicesKit": "5.0.0+",
    "@kit.AbilityKit": "5.0.0+",
    "@kit.PerformanceAnalysisKit": "5.0.0+"
  },
  "buildOption": {
    " Harcourt ": "shared"
  }
}

7.2 宿主APP的oh-package.json5依赖配置

json 复制代码
{
  "dependencies": {
    "mapnavigation": "file:../mapnavigation"
  }
}

7.3 模块导出配置(index.ets)

typescript 复制代码
// mapnavigation/src/main/ets/index.ets

// 导出所有公共API
export { NavigationKit, createNavigationKit } from './NavigationKit'
export type { 
  Destination, 
  NavigationConfig, 
  NavigationTheme, 
  NavigationEvent 
} from './NavigationKit'

// 导出组件(高级用户可直接使用)
export { MapView } from './components/MapView'

// 导出服务(高级用户可直接使用)
export { NavigationService } from './services/NavigationService'

八、常见问题与解决方案

8.1 真机调试地图不显示

问题表现:模拟器或真机运行时地图区域为空白

排查步骤

  1. 确认AGC已开通Map Kit服务并获取API Key
  2. 检查module.json5metadata配置是否正确
  3. 确保已正确签名(地图服务需要华为主题签名)
  4. 检查设备是否为HarmonyOS 6.0+真机

解决方案

json 复制代码
// module.json5中添加API Key配置
{
  "app": {
    "bundleName": "com.example.mapdemo",
    "metadata": [
      {
        "name": "client_id",
        "value": "your_api_key_here"
      }
    ]
  }
}

8.2 模拟器无法测试地图

原因:Map Kit需要真实的地图数据服务,模拟器不提供此服务

解决方案

  1. 使用真机进行地图功能测试
  2. build()中添加模拟器检测逻辑
typescript 复制代码
if (!canUseMapOnSimulator()) {
  Column() {
    Text('地图功能需要在真机上测试')
    Text('请连接HarmonyOS 6.0+设备')
  }
} else {
  MapComponent({ ... })
}

8.3 坐标偏移问题

问题:标记点位置与实际位置有偏移

原因:不同地图服务商使用不同坐标系

解决方案:统一使用GCJ02坐标系(鸿蒙Map Kit国内默认)

typescript 复制代码
// GPS坐标转GCJ02
function wgs84ToGcj02(lat: number, lng: number): { latitude: number, longitude: number } {
  // 已在CoordinateConverter中实现
}

8.4 权限申请被拒绝

问题:用户拒绝定位权限后地图无法使用

解决方案

typescript 复制代码
import { bundleManager, permissionFlags } from '@kit.AbilityKit'

async function checkAndRequestLocation(): Promise<boolean> {
  // 检查权限
  let state = await permissionManager.checkGrantPermissionForResult(
    getContext(this),
    'ohos.permission.LOCATION',
    permissionFlags.PERMISSION_USER_STRICTLY_REQUIRED
  )
  
  if (state === permissionManager.GrantStatus.PERMISSION_GRANT) {
    return true
  }
  
  // 申请权限
  return await permissionManager.requestPermissionOnUser(
    getContext(this),
    ['ohos.permission.LOCATION']
  )
}

九、总结

本文详细介绍了鸿蒙6.0地图导航应用开发方案,重点阐述了如何将地图导航能力封装为可复用的模块组件。核心要点包括:

  1. 完整的技术实现:从环境配置、权限申请到地图渲染、导航调起,覆盖开发全流程
  2. 模块化架构设计:通过NavigationKit统一入口,实现地图能力的即插即用
  3. 一行代码集成:宿主应用只需几行配置即可拥有完整导航功能
  4. 丰富的配置选项:支持地图类型切换、主题定制、事件回调等高级功能
  5. 完善的错误处理:提供常见问题的解决方案

通过本文的设计,地图导航模块可以作为独立的HAR包发布,其他应用只需添加依赖即可复用。这种设计模式符合鸿蒙生态的模块化理念,能够大幅提升开发效率,避免重复造轮子。

相关推荐
UnicornDev6 小时前
【HarmonyOS 6】底部悬浮导航的沉浸光感适配(API23)
华为·harmonyos·arkts·鸿蒙·鸿蒙系统
轻口味6 小时前
HarmonyOS 6 轻相机应用开发5:实时自动戴眼镜功能实现
数码相机·华为·harmonyos
maaath7 小时前
【maaath】Flutter for OpenHarmony 实战:构建跨平台房产租售应用
flutter·华为·harmonyos
骆驼10247 小时前
华为AC+FIT AP典型组网部署配置
华为·hcia·ac+ap
枫叶丹47 小时前
【HarmonyOS 6.0】Camera Kit白平衡API深度解析:让三方应用真正“掌控”色彩
开发语言·华为·harmonyos·视频编解码
maaath7 小时前
【maaath】Flutter for OpenHarmony 游戏中心应用实战开发
flutter·游戏·华为·harmonyos
枫叶丹47 小时前
【HarmonyOS 6.0】Camera Kit 新增系统性能压力监听功能全解析
开发语言·计算机视觉·华为·harmonyos
xmdy58667 小时前
Flutter+开源鸿蒙实战|智安盾电商溯源平台Day4 合规检测功能开发+个人中心框架搭建
flutter·开源·harmonyos
xmdy58668 小时前
Flutter+开源鸿蒙实战|智联邻里Day4 底部导航栏+邻里互助页面+闲置发布表单+本地缓存
flutter·开源·harmonyos