第8篇:地图标记与可视化

在这里插入图片描述

教程目标

通过本篇教程,你将学会:

  • 创建地图服务,处理地图相关业务逻辑
  • 在地图上展示地块标记
  • 实现标记颜色与状态管理
  • 处理标记点击事件
  • 实现地图与地块数据联动
  • 智能计算地图中心点和缩放级别

完成本教程后,地块数据将在地图上可视化展示。


一、创建地图数据模型

在实现地图功之前,我们需要定义地图相关的数据模型。

1.1 创建 MapModels.ets

操作步骤

  1. entry/src/main/ets/models/ 目录下创建新文件
  2. 命名为 MapModels.ets
  3. 输入以下代码
/**
 * 地图相关数据模型
 * 包含地图位置、标记、统计等结构
 */

/**
 * 地图位置坐标
 */
export interface MapLocation {
  latitude: number;   // 纬度,范围:-90 到 90
  longitude: number;  // 经度,范围:-180 到 180
}

/**
 * 地块地图标记
 * 用于在地图上显示地块位置
 */
export interface FieldMapMarker {
  id: string;                  // 地块ID
  name: string;                // 地块名称
  location: MapLocation;       // 坐标位置
  area: number;                // 面积(亩)
  status: FieldMarkerStatus;   // 标记状态
  color: string;               // 标记颜色(十六进制)
  description?: string;         // 描述信息
}

/**
 * 地块标记状态枚举
 */
export enum FieldMarkerStatus {
  PLANTING = 'planting',      // 种植中 - 绿色
  ATTENTION = 'attention',    // 需关注 - 橙色
  URGENT = 'urgent',          // 紧急 - 红色
  IDLE = 'idle'               // 闲置 - 蓝色
}

/**
 * 地图视图配置
 */
export interface MapViewConfig {
  center: MapLocation;       // 地图中心点
  zoom: number;              // 缩放级别(3-20)
  mapType: number;           // 地图类型
}

/**
 * 地图统计信息
 */
export interface MapStatistics {
  totalFields: number;        // 地块总数
  plantingFields: number;     // 种植中数量
  idleFields: number;         // 闲置数量
  totalArea: number;          // 总面积
}

二、创建地图服务

地图服务负责处理地图相关的业务逻辑,包括数据转换、颜色映射、智能计算等功能。

2.1 创建 MapService.ets

操作步骤

  1. entry/src/main/ets/services/ 目录下创建新文件
  2. 命名为 MapService.ets
  3. 输入以下代码
import { FieldInfo } from '../models/ProfessionalAgricultureModels';
import { FieldMapMarker, FieldMarkerStatus, MapLocation } from '../models/MapModels';

/**
 * 地图服务
 * 负责地图相关的业务逻辑和数据转换
 * 提供地块数据与地图标记的转换功能
 */
export class MapService {
  // 单例实例
  private static instance: MapService;

  // 私有构造函数,防止外部直接创建实例
  private constructor() {}

  /**
   * 获取单例实例
   * @returns MapService 单例对象
   */
  static getInstance(): MapService {
    if (!MapService.instance) {
      MapService.instance = new MapService();
    }
    return MapService.instance;
  }

  /**
   * 将地块信息列表转换为地图标记列表
   * 只有包含坐标的地块才会被转换
   * @param fields 地块信息列表
   * @returns 地图标记列表
   */
  convertFieldsToMarkers(fields: FieldInfo[]): FieldMapMarker[] {
    const markers: FieldMapMarker[] = [];

    for (const field of fields) {
      // 只转换有经纬度坐标的地块
      if (field.latitude !== undefined && field.longitude !== undefined) {
        const marker: FieldMapMarker = {
          id: field.id,
          name: field.name,
          location: {
            latitude: field.latitude,
            longitude: field.longitude
          },
          area: field.area,
          status: this.getFieldStatus(field),
          color: this.getFieldColor(field),
          description: this.getFieldDescription(field)
        };

        markers.push(marker);
      }
    }

    console.info(`[MapService] Converted ${markers.length}/${fields.length} fields to markers`);
    return markers;
  }

  /**
   * 获取地块对应的标记状态
   * @param field 地块信息
   * @returns 标记状态枚举
   */
  private getFieldStatus(field: FieldInfo): FieldMarkerStatus {
    switch (field.status) {
      case '种植中':
        return FieldMarkerStatus.PLANTING;   // 绿色标记
      case '闲置':
        return FieldMarkerStatus.IDLE;       // 蓝色标记
      case '准备中':
        return FieldMarkerStatus.ATTENTION; // 橙色标记
      case '需关注':
        return FieldMarkerStatus.ATTENTION; // 橙色标记
      default:
        return FieldMarkerStatus.IDLE;       // 默认蓝色
    }
  }

  /**
   * 根据地块状态获取标记颜色
   * 使用十六进制颜色值,与状态语义相匹配
   * @param field 地块信息
   * @returns 颜色字符串(十六进制)
   */
  private getFieldColor(field: FieldInfo): string {
    switch (field.status) {
      case '种植中':
        return '#52C41A';  // 绿色 - 代表生机
      case '闲置':
        return '#1890FF';  // 蓝色 - 代表平静
      case '准备中':
        return '#FAAD14';  // 橙色 - 代表准备
      case '需关注':
        return '#FA8C16';  // 深橙色 - 代表警告
      default:
        return '#8F959E';  // 灰色 - 默认
    }
  }

  /**
   * 生成地块的描述信息
   * @param field 地块信息
   * @returns 描述字符串
   */
  private getFieldDescription(field: FieldInfo): string {
    let desc = `面积: ${field.area}亩\n`;
    desc += `位置: ${field.location}\n`;

    // 如果有作物信息,添加到描述中
    if (field.currentCrop) {
      desc += `作物: ${field.currentCrop.name}\n`;
      desc += `阶段: ${this.getGrowthStageLabel(field.currentCrop.growthStage)}`;
    }

    return desc.trim();
  }

  /**
   * 获取生长阶段中文标签
   */
  private getGrowthStageLabel(stage: string): string {
    const stages: Record<string, string> = {
      'seedling': '育苗期',
      'growing': '生长期',
      'flowering': '开花期',
      'fruiting': '结果期',
      'harvesting': '收获期',
      'fallow': '休耕期'
    };
    return stages[stage] || stage;
  }

  /**
   * 计算所有地块的地理中心点
   * 取所有地块经纬度的平均值作为中心
   * @param fields 地块列表
   * @returns 地图中心点,如果没有有效地块返回null
   */
  calculateMapCenter(fields: FieldInfo[]): MapLocation | null {
    // 筛选出有坐标的地块
    const validFields = fields.filter(f => f.latitude && f.longitude);

    if (validFields.length === 0) {
      return null;  // 没有有效地块
    }

    // 计算平均纬度和经度
    const sumLat = validFields.reduce((sum, f) => sum + (f.latitude || 0), 0);
    const sumLng = validFields.reduce((sum, f) => sum + (f.longitude || 0), 0);

    return {
      latitude: sumLat / validFields.length,
      longitude: sumLng / validFields.length
    };
  }

  /**
   * 计算合适的地图缩放级别
   * 根据地块的分布范围自动计算最佳缩放级别
   * @param fields 地块列表
   * @returns 缩放级别(3-20)
   */
  calculateZoomLevel(fields: FieldInfo[]): number {
    const validFields = fields.filter(f => f.latitude && f.longitude);

    if (validFields.length === 0) {
      return 12;  // 默认缩放级别
    }

    if (validFields.length === 1) {
      return 15;  // 单个地块,放大查看
    }

    // 提取所有纬度和经度
    const lats = validFields.map(f => f.latitude!);
    const lngs = validFields.map(f => f.longitude!);

    // 计算经纬度的范围
    const latRange = Math.max(...lats) - Math.min(...lats);
    const lngRange = Math.max(...lngs) - Math.min(...lngs);

    // 取较大值作为分布范围
    const maxRange = Math.max(latRange, lngRange);

    // 根据分布范围计算缩放级别
    // 范围越大,缩放级别越小(显示更大区域)
    if (maxRange > 1) return 8;     // 大范围:省市级
    if (maxRange > 0.5) return 10;   // 中等范围:城市级
    if (maxRange > 0.1) return 12;   // 小范围:区县级
    if (maxRange > 0.05) return 14;  // 很小范围:乡镇级
    return 16;                            // 极小范围:村级
  }

  /**
   * 检查地块是否有坐标信息
   * @param field 地块信息
   * @returns 是否有坐标
   */
  hasLocation(field: FieldInfo): boolean {
    return field.latitude !== undefined &&
           field.longitude !== undefined &&
           !isNaN(field.latitude) &&
           !isNaN(field.longitude);
  }

  /**
   * 计算两个坐标点之间的距离(单位:公里)
   * 使用 Haversine 公式计算球面距离
   * @param lat1 第一个点的纬度
   * @param lng1 第一个点的经度
   * @param lat2 第二个点的纬度
   * @param lng2 第二个点的经度
   * @returns 距离(公里)
   */
  calculateDistance(lat1: number, lng1: number, lat2: number, lng2: number): number {
    const R = 6371; // 地球半径,单位:公里

    // 转换为弧度
    const dLat = this.toRadians(lat2 - lat1);
    const dLng = this.toRadians(lng2 - lng1);

    const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
            Math.cos(this.toRadians(lat1)) * Math.cos(this.toRadians(lat2)) *
            Math.sin(dLng / 2) * Math.sin(dLng / 2);

    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    return R * c;
  }

  /**
   * 将角度转换为弧度
   */
  private toRadians(degrees: number): number {
    return degrees * (Math.PI / 180);
  }
}

服务设计要点

功能 说明
单例模式 确保全局只有一个地图服务实例
数据转换 将业务地块数据转换为地图标记数据
颜色映射 根据状态自动分配对应的标记颜色
智能计算 自动计算最佳地图中心和缩放级别
坐标过滤 只处理有坐标的地块,避免错误

三、更新地图首页

现在我们需要更新地图首页,添加地块标记功能。

3.1 修改 FieldMapPage.ets

操作步骤

  1. 打开 entry/src/main/ets/pages/Map/FieldMapPage.ets
  2. 用以下代码替换现有内容
import { Map, MapOptions, Marker, LatLng } from '@amap/amap_lbs_map3d';
import { AMapLocationService, LocationResult } from '../../services/AMapLocationService';
import { MapService } from '../../services/MapService';
import { FieldService, FieldStats } from '../../services/FieldService';
import { MapConstants } from '../../constants/MapConstants';
import { FieldInfo } from '../../models/ProfessionalAgricultureModels';
import { FieldMapMarker, MapLocation } from '../../models/MapModels';
import { common } from '@kit.AbilityKit';
import { router } from '@kit.ArkUI';
import { CommonCard, InfoRow, StatusBadge, LoadingView, getBadgeStyle, BadgeType } from '../../components/CommonComponents';

/**
 * 地图首页
 * 以地图为核心,展示地块标记,支持定位、导航等操作
 */
@Entry
@ComponentV2
struct FieldMapPage {
  // 状态变量
  @Local isLoading: boolean = true;                    // ��载状态
  @Local currentLocation: LocationResult | null = null;   // 当前位置
  @Local fields: FieldInfo[] = [];                        // 地块列表
  @Local markers: FieldMapMarker[] = [];                  // 地图标记
  @Local selectedField: FieldInfo | null = null;         // 选中的地块
  @Local showFieldInfo: boolean = false;                   // 显示地块信息卡片
  @Local stats: FieldStats | null = null;                  // 统计信息

  // 地图相关
  private mapController: Map | null = null;               // 地图控制器
  private mapService = MapService.getInstance();
  private fieldService = FieldService.getInstance();
  private locationService = AMapLocationService.getInstance();
  private context: common.UIAbilityContext | null = null;

  /**
   * 页面初始化
   */
  async aboutToAppear(): Promise<void> {
    // 获取上下文
    this.context = getContext(this) as common.UIAbilityContext;
    // 初始化定位服务
    this.locationService.initialize(this.context);
    // 加载数据
    await this.loadFields();
    // 请求定位
    await this.requestLocation();
  }

  build() {
    Column() {
      // 地图容器
      Stack() {
        // 地图层
        Column() {
          Map({
            mapOptions: this.getMapOptions(),
            onMapReady: (map: Map) => {
              this.onMapReady(map);
            }
          })
          .width('100%')
          .height('100%')
        }

        // 地块信息卡片(底部弹出)
        if (this.showFieldInfo && this.selectedField) {
          Column() {
            this.buildFieldInfoCard()
          }
          .width('100%')
          .padding(16)
          .justifyContent(FlexAlign.End)
        }

        // 右侧工具栏
        Column() {
          this.buildToolbar()
        }
        .width('100%')
        .height('100%')
        .justifyContent(FlexAlign.End)
        .padding(16)

        // 加载指示器
        if (this.isLoading) {
          Column() {
            LoadingProgress()
              .width(40)
              .height(40)
              .color($r('app.color.primary_professional'))
          }
          .width('100%')
          .height('100%')
          .justifyContent(FlexAlign.Center)
          .backgroundColor('#80000000')
        }
      }
    }
    .width('100%')
    .height('100%')
  }

  /**
   * 获取地图配置选项
   */
  private getMapOptions(): MapOptions {
    // 计算地图中心点:优先使用地块中心,否则使用默认中心
    const center = this.mapService.calculateMapCenter(this.fields);
    const zoom = this.mapService.calculateZoomLevel(this.fields);

    return {
      center: center || {
        latitude: MapConstants.DEFAULT_CENTER_LATITUDE,
        longitude: MapConstants.DEFAULT_CENTER_LONGITUDE
      },
      zoom: zoom || MapConstants.DEFAULT_ZOOM,
      mapType: MapConstants.MAP_TYPE_NORMAL
    };
  }

  /**
   * 地图准备完成回调
   */
  private onMapReady(map: Map): void {
    console.info('[FieldMapPage] Map ready, adding markers');
    this.mapController = map;
    this.addFieldMarkers();
    this.isLoading = false;
  }

  /**
   * 加载地块数据
   */
  private async loadFields(): Promise<void> {
    try {
      this.fields = await this.fieldService.getAllFields();
      this.stats = await this.fieldService.getFieldStats();
      // 转换为地图标记
      this.markers = this.mapService.convertFieldsToMarkers(this.fields);
      console.info(`[FieldMapPage] Loaded ${this.fields.length} fields, ${this.markers.length} markers`);
    } catch (error) {
      console.error('[FieldMapPage] Load fields failed:', error);
    }
  }

  /**
   * 添加地块标记到地图
   */
  private addFieldMarkers(): void {
    if (!this.mapController || this.markers.length === 0) {
      console.warn('[FieldMapPage] No markers to add');
      return;
    }

    // 清除旧标记
    this.mapController.clearMarkers();

    // 添加新标记
    this.markers.forEach(marker => {
      // 创建高德地图标记对象
      const mapMarker = new Marker({
        position: new LatLng(marker.location.latitude, marker.location.longitude),
        title: marker.name,
        snippet: marker.description
      });

      // 绑定标记点击事件
      mapMarker.on('click', () => {
        this.onMarkerClick(marker.id);
      });

      // 将标记添加到地图
      this.mapController!.addMarker(mapMarker);
    });

    console.info(`[FieldMapPage] Added ${this.markers.length} markers to map`);
  }

  /**
   * 标记点击事件处理
   */
  private onMarkerClick(fieldId: string): void {
    console.info('[FieldMapPage] Marker clicked:', fieldId);

    // 查找对应的地块
    const field = this.fields.find(f => f.id === fieldId);
    if (field) {
      this.selectedField = field;
      this.showFieldInfo = true;
      console.info('[FieldMapPage] Selected field:', field.name);
    }
  }

  /**
   * 构建地块信息卡片
   */
  @Builder
  buildFieldInfoCard() {
    CommonCard({
      title: this.selectedField!.name,
      icon: '🌾',
      onClose: () => {
        this.showFieldInfo = false;
      }
    }) {
      Column({ space: 12 }) {
        // 基本信息
        InfoRow({
          label: '面积',
          value: `${this.selectedField!.area}`
        })

        // 状态信息
        Row() {
          Text('状态')
            .fontSize(14)
            .fontColor($r('app.color.text_secondary'))

          Blank()

          StatusBadge({
            text: this.selectedField!.status,
            textColor: this.getStatusColor(this.selectedField!.status).textColor,
            backgroundColor: this.getStatusColor(this.selectedField!.status).backgroundColor
          })
        }
        .width('100%')

        // 位置信息
        InfoRow({
          label: '位置',
          value: this.selectedField!.location
        })

        // 如果有坐标,显示坐标
        if (this.selectedField!.latitude && this.selectedField!.longitude) {
          InfoRow({
            label: '坐标',
            value: `${this.selectedField!.latitude.toFixed(4)}, ${this.selectedField!.longitude.toFixed(4)}`
          })
        }

        // 当前作物信息
        if (this.selectedField!.currentCrop) {
          InfoRow({
            label: '当前作物',
            value: this.selectedField!.currentCrop.name
          })
          InfoRow({
            label: '生长阶段',
            value: this.getGrowthStageLabel(this.selectedField!.currentCrop.growthStage)
          })
        }

        // 操作按钮
        Row({ space: 12 }) {
          Button('查看详情')
            .fontSize(14)
            .backgroundColor($r('app.color.primary_professional'))
            .layoutWeight(1)
            .onClick(() => {
              router.pushUrl({
                url: 'pages/Management/FieldDetailPage',
                params: { fieldId: this.selectedField!.id }
              });
            })

          Button('关闭')
            .fontSize(14)
            .backgroundColor($r('app.color.background'))
            .fontColor($r('app.color.text_primary'))
            .layoutWeight(1)
            .onClick(() => {
              this.showFieldInfo = false;
            })
        }
      }
    }
  }

  /**
   * 获取生长阶段标签
   */
  private getGrowthStageLabel(stage: string): string {
    const stages: Record<string, string> = {
      'seedling': '育苗期',
      'growing': '生长期',
      'flowering': '开花期',
      'fruiting': '结果期',
      'harvesting': '收获期',
      'fallow': '休耕期'
    };
    return stages[stage] || stage;
  }

  /**
   * 获取状态颜色
   */
  private getStatusColor(status: string): { textColor: string, backgroundColor: string } {
    switch (status) {
      case '种植中':
        return getBadgeStyle(BadgeType.SUCCESS);
      case '闲置':
        return getBadgeStyle(BadgeType.INFO);
      case '准备中':
        return getBadgeStyle(BadgeType.WARNING);
      default:
        return getBadgeStyle(BadgeType.DEFAULT);
    }
  }

  /**
   * 构建工具栏
   */
  @Builder
  buildToolbar() {
    Column({ space: 12 }) {
      // 定位按钮
      Button() {
        Text('📍').fontSize(24)
      }
      .width(48)
      .height(48)
      .backgroundColor($r('app.color.card_background'))
      .borderRadius(24)
      .shadow({
        radius: 8,
        color: 'rgba(0, 0, 0, 0.1)'
      })
      .onClick(() => {
        this.requestLocation();
      })

      // 刷新按钮
      Button() {
        Text('🔄').fontSize(24)
      }
      .width(48)
      .height(48)
      .backgroundColor($r('app.color.card_background'))
      .borderRadius(24)
      .shadow({
        radius: 8,
        color: 'rgba(0, 0, 0, 0.1)'
      })
      .onClick(() => {
        this.refreshData();
      })

      // 地块列表按钮
      Button() {
        Text('📋').fontSize(24)
      }
      .width(48)
      .height(48)
      .backgroundColor($r('app.color.card_background'))
      .borderRadius(24)
      .shadow({
        radius: 8,
        color: 'rgba(0, 0, 0, 0.1)'
      })
      .onClick(() => {
        router.pushUrl({
          url: 'pages/Management/FieldManagementPage'
        });
      })

      // 添加地块按钮
      Button() {
        Text('➕').fontSize(24)
      }
      .width(48)
      .height(48)
      .backgroundColor($r('app.color.primary_professional'))
      .borderRadius(24)
      .shadow({
        radius: 8,
        color: 'rgba(82, 196, 26, 0.3)'
      })
      .onClick(() => {
        router.pushUrl({
          url: 'pages/Management/AddFieldPage'
        });
      })
    }
    .alignItems(HorizontalAlign.End)
  }

  /**
   * 请求当前位置
   */
  private async requestLocation(): Promise<void> {
    await this.locationService.getCurrentLocation({
      onSuccess: (location: LocationResult) => {
        console.info('[FieldMapPage] Location obtained:', location.latitude, location.longitude);
        this.currentLocation = location;
        // 移动地图到当前位置
        if (this.mapController && this.currentLocation) {
          this.mapController.moveCamera({
            target: new LatLng(this.currentLocation.latitude, this.currentLocation.longitude),
            zoom: 15
          });
        }
      },
      onError: (error: string) => {
        console.error('[FieldMapPage] Location error:', error);
      }
    });
  }

  /**
   * 刷新数据
   */
  private async refreshData(): Promise<void> {
    this.isLoading = true;
    await this.loadFields();
    // 重新添加标记
    if (this.mapController) {
      this.addFieldMarkers();
    }
    this.isLoading = false;
  }
}

四、配置页面路由

操作步骤

  1. 打开 entry/src/main/resources/base/profile/main_pages.json
  2. 确认已包含 pages/Map/FieldMapPage
{
  "src": [
    "pages/WelcomePage",
    "pages/Index",
    "pages/Map/FieldMapPage",
    "pages/Management/FieldManagementPage",
    "pages/Management/AddFieldPage",
    "pages/Management/EditFieldPage",
    "pages/Management/FieldDetailPage",
    "pages/OnboardingFlow/ModeSelectionPage",
    "pages/OnboardingFlow/LocationPage",
    "pages/OnboardingFlow/GoalsPage"
  ]
}

五、运行与测试

5.1 测试准备

确保测试数据包含坐标

检查测试地块是否有经纬度坐标,如果没有坐标,标记将无法显示:

// 在测试数据中添加坐标
latitude: 30.6228,
longitude: 114.1377

5.2 测试步骤

  1. 启动应用

    • 点击运行按钮或按 Shift + F10
  2. 进入地图页面

    • 完成引导流程后进入地图首页
  3. 查看地块标记

    • 地图上应显示蓝色/绿色标记
    • 标记位置对应地块的经纬度
  4. 测试标记点击

    • 点击地图上的标记
    • 底部弹出地块信息卡片
    • 显示地块名称、面积、状态、位置等信息
  5. 测试定位功能

    • 点击"定位"按钮(📍)
    • 地图应移动到当前位置
    • 缩放级别设置为15
  6. 测试查看详情

    • 在信息卡片中点击"查看详情"
    • 应跳转到地块详情页面
  7. 测试工具栏

    • 点击"刷新"按钮(🔄)重新加载数据
    • 点击"列表"按钮(📋)进入地块列表
    • 点击"添加"按钮(➕)进入添加页面

5.3 预期效果

功能 预期效果
地图显示 地图正常加载,显示基础地图
地块标记 有坐标的地块显示为标记
标记颜色 种植中=绿色,闲置=蓝色
标记点击 点击后弹出地块信息卡片
定位功能 地图移动到当前位置
数据刷新 点击刷新后重新加载标记

六、常见问题与解决方案

6.1 地图不显示

问题:运行后地图区域空白

解决方案

  1. 检查高德地图API Key是否正确配置
  2. 确认网络连接正常
  3. 查看控制台是否有SDK初始化错误
  4. 验证 MapsInitializer.setApiKey() 已在 EntryAbility 中调用

6.2 标记不显示

问题:地图正常但地块标记不显示

解决方案

  1. 确认测试数据包含经纬度坐标
  2. 检查 convertFieldsToMarkers() 方法的逻辑
  3. 使用 console.log 检查 markers 数组长度
  4. 验证 addMarker() 方法是否被调用

6.3 标记点击无响应

问题:点击标记后没有反应

解决方案

  1. 检查事件绑定是否正确 mapMarker.on('click', ...)
  2. 确认 onMarkerClick 方法中的逻辑
  3. 在方法中添加 console.log 调试
  4. 检查 selectedField 是否正确设置

6.4 地图中心位置不正确

问题:地图中心偏离所有地块

解决方案

  1. 检查 calculateMapCenter() 方法的计算逻辑
  2. 确认传入的字段列表不为空
  3. 使用调试输出查看计算的经纬度值
  4. 检查是否有坐标字段的数据

6.5 缩放级别不合适

问题:标记太密集或太分散

解决方案

  1. 检查 calculateZoomLevel() 方法的范围判断
  2. 根据实际分布情况调整阈值
  3. 可以在地图页面添加缩放控制按钮
  4. 允许用户手动调整缩放级别

七、总结

本篇教程完成了:

  • ✅ 地图数据模型设计
  • ✅ 地图服务实现(数据转换、颜色映射、智能计算)
  • ✅ 地图标记展示功能
  • ✅ 标记点击事件处理
  • ✅ 地块信息卡片
  • ✅ 地图工具栏(定位、刷新、列表、添加)
  • ✅ 地图与地块数据联动

关键技术点

技术点 说明
高德地图SDK Map 组件、Marker 标记
数据转换 业务数据 → 地图标记
颜色映射 状态 → 颜色的自动关联
智能计算 自动计算最佳地图中心和缩放
事件处理 标记点击、工具栏操作
状态管理 选中地块、信息卡片显示

地图服务核心功能

功能 方法名 说明
数据转换 convertFieldsToMarkers 地块→标记
颜色获取 getFieldColor 状态→颜色
中心计算 calculateMapCenter 自动计算地图中心
缩放计算 calculateZoomLevel 自动计算缩放级别
坐标检查 hasLocation 检查是否有坐标
距离计算 calculateDistance 计算两点距离

八、下一步

在下一篇教程中,我们将学习:

  • 作物数据模型设计
  • 作物种植记录管理
  • 作物生长阶段管理
  • 作物健康状态跟踪
  • 作物与地块关联

准备工作

  • 了解作物生长周期的各个阶段
  • 准备作物测试数据
  • 熟悉作物与地块的关联关系

参考资料


教程版本:v1.0
更新日期:2026-01
适用版本:DevEco Studio 5.0+, HarmonyOS API 17+

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐