HarmonyOS智慧农业管理应用开发教程--高高种地---第8篇:地图标记与可视化
✅ 地图数据模型设计✅ 地图服务实现(数据转换、颜色映射、智能计算)✅ 地图标记展示功能✅ 标记点击事件处理✅ 地块信息卡片✅ 地图工具栏(定位、刷新、列表、添加)✅ 地图与地块数据联动关键技术点技术点说明高德地图SDKMap 组件、Marker 标记数据转换业务数据 → 地图标记颜色映射状态 → 颜色的自动关联智能计算自动计算最佳地图中心和缩放事件处理标记点击、工具栏操作状态管理选中地块、信息卡
·
第8篇:地图标记与可视化

教程目标
通过本篇教程,你将学会:
- 创建地图服务,处理地图相关业务逻辑
- 在地图上展示地块标记
- 实现标记颜色与状态管理
- 处理标记点击事件
- 实现地图与地块数据联动
- 智能计算地图中心点和缩放级别
完成本教程后,地块数据将在地图上可视化展示。
一、创建地图数据模型
在实现地图功之前,我们需要定义地图相关的数据模型。
1.1 创建 MapModels.ets
操作步骤:
- 在
entry/src/main/ets/models/目录下创建新文件 - 命名为
MapModels.ets - 输入以下代码
/**
* 地图相关数据模型
* 包含地图位置、标记、统计等结构
*/
/**
* 地图位置坐标
*/
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
操作步骤:
- 在
entry/src/main/ets/services/目录下创建新文件 - 命名为
MapService.ets - 输入以下代码
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
操作步骤:
- 打开
entry/src/main/ets/pages/Map/FieldMapPage.ets - 用以下代码替换现有内容
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;
}
}
四、配置页面路由
操作步骤:
- 打开
entry/src/main/resources/base/profile/main_pages.json - 确认已包含
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 测试步骤
-
启动应用
- 点击运行按钮或按
Shift + F10
- 点击运行按钮或按
-
进入地图页面
- 完成引导流程后进入地图首页
-
查看地块标记
- 地图上应显示蓝色/绿色标记
- 标记位置对应地块的经纬度
-
测试标记点击
- 点击地图上的标记
- 底部弹出地块信息卡片
- 显示地块名称、面积、状态、位置等信息
-
测试定位功能
- 点击"定位"按钮(📍)
- 地图应移动到当前位置
- 缩放级别设置为15
-
测试查看详情
- 在信息卡片中点击"查看详情"
- 应跳转到地块详情页面
-
测试工具栏
- 点击"刷新"按钮(🔄)重新加载数据
- 点击"列表"按钮(📋)进入地块列表
- 点击"添加"按钮(➕)进入添加页面
5.3 预期效果
| 功能 | 预期效果 |
|---|---|
| 地图显示 | 地图正常加载,显示基础地图 |
| 地块标记 | 有坐标的地块显示为标记 |
| 标记颜色 | 种植中=绿色,闲置=蓝色 |
| 标记点击 | 点击后弹出地块信息卡片 |
| 定位功能 | 地图移动到当前位置 |
| 数据刷新 | 点击刷新后重新加载标记 |
六、常见问题与解决方案
6.1 地图不显示
问题:运行后地图区域空白
解决方案:
- 检查高德地图API Key是否正确配置
- 确认网络连接正常
- 查看控制台是否有SDK初始化错误
- 验证
MapsInitializer.setApiKey()已在EntryAbility中调用
6.2 标记不显示
问题:地图正常但地块标记不显示
解决方案:
- 确认测试数据包含经纬度坐标
- 检查
convertFieldsToMarkers()方法的逻辑 - 使用 console.log 检查 markers 数组长度
- 验证
addMarker()方法是否被调用
6.3 标记点击无响应
问题:点击标记后没有反应
解决方案:
- 检查事件绑定是否正确
mapMarker.on('click', ...) - 确认 onMarkerClick 方法中的逻辑
- 在方法中添加 console.log 调试
- 检查 selectedField 是否正确设置
6.4 地图中心位置不正确
问题:地图中心偏离所有地块
解决方案:
- 检查
calculateMapCenter()方法的计算逻辑 - 确认传入的字段列表不为空
- 使用调试输出查看计算的经纬度值
- 检查是否有坐标字段的数据
6.5 缩放级别不合适
问题:标记太密集或太分散
解决方案:
- 检查
calculateZoomLevel()方法的范围判断 - 根据实际分布情况调整阈值
- 可以在地图页面添加缩放控制按钮
- 允许用户手动调整缩放级别
七、总结
本篇教程完成了:
- ✅ 地图数据模型设计
- ✅ 地图服务实现(数据转换、颜色映射、智能计算)
- ✅ 地图标记展示功能
- ✅ 标记点击事件处理
- ✅ 地块信息卡片
- ✅ 地图工具栏(定位、刷新、列表、添加)
- ✅ 地图与地块数据联动
关键技术点:
| 技术点 | 说明 |
|---|---|
| 高德地图SDK | Map 组件、Marker 标记 |
| 数据转换 | 业务数据 → 地图标记 |
| 颜色映射 | 状态 → 颜色的自动关联 |
| 智能计算 | 自动计算最佳地图中心和缩放 |
| 事件处理 | 标记点击、工具栏操作 |
| 状态管理 | 选中地块、信息卡片显示 |
地图服务核心功能:
| 功能 | 方法名 | 说明 |
|---|---|---|
| 数据转换 | convertFieldsToMarkers | 地块→标记 |
| 颜色获取 | getFieldColor | 状态→颜色 |
| 中心计算 | calculateMapCenter | 自动计算地图中心 |
| 缩放计算 | calculateZoomLevel | 自动计算缩放级别 |
| 坐标检查 | hasLocation | 检查是否有坐标 |
| 距离计算 | calculateDistance | 计算两点距离 |
八、下一步
在下一篇教程中,我们将学习:
- 作物数据模型设计
- 作物种植记录管理
- 作物生长阶段管理
- 作物健康状态跟踪
- 作物与地块关联
准备工作:
- 了解作物生长周期的各个阶段
- 准备作物测试数据
- 熟悉作物与地块的关联关系
参考资料
教程版本:v1.0
更新日期:2026-01
适用版本:DevEco Studio 5.0+, HarmonyOS API 17+
更多推荐


所有评论(0)