第20篇:天气服务与气象数据
📚 本篇导读
天气对农业生产至关重要,及时准确的天气信息可以帮助农户做出正确的农事决策。本篇教程将详细讲解如何集成高德地图的天气API,实现实时天气查询、天气预报展示,并结合农业场景提供智能的农事建议。
本篇将实现:
- 🌤️ 实时天气查询(基于高德地图Weather API)
- 📅 天气预报展示(7天预报、24小时预报)
- ⚠️ 气象预警功能(恶劣天气提醒)
- 🌾 农事建议关联(根据天气推荐农事活动)
- 💾 天气数据缓存(减少API调用,提升性能)
🎯 学习目标
完成本篇教程后,你将掌握:
- 如何使用高德地图Weather API查询天气
- 如何设计和实现天气数据模型
- 如何展示实时天气和天气预报
- 如何根据天气提供农事建议
- 天气数据的缓存策略
一、天气服务架构设计
1.1 功能架构
天气服务模块
├── 数据获取层
│ ├── 高德Weather API(实时天气)
│ ├── 逆地理编码(获取城市名)
│ └── 数据缓存(减少API调用)
│
├── 数据处理层
│ ├── 天气数据解析
│ ├── 天气图标映射
│ └── 农事建议生成
│
├── 展示层
│ ├── 地图页天气卡片
│ ├── 天气详情页面
│ └── 天气预报列表
│
└── 应用层
├── 农事提醒(根据天气)
├── 浇水建议(根据降雨)
└── 病虫害预警(根据温湿度)
1.2 数据流程
用户定位
↓
逆地理编码获取城市
↓
查询天气API
↓
解析天气数据
↓
缓存天气信息
↓
展示天气 + 生成农事建议
↓
语音播报(可选)
1.3 数据模型
实时天气接口:
typescript
/**
* 当前天气信息
*/
export interface CurrentWeather {
temperature: number; // 当前温度(℃)
feelsLike: number; // 体感温度(℃)
weatherType: WeatherType; // 天气类型
weatherDesc: string; // 天气描述
humidity: number; // 湿度(%)
windSpeed: number; // 风速(km/h)
windDirection: string; // 风向
pressure: number; // 气压(hPa)
uvIndex: number; // 紫外线指数
visibility: number; // 可见度(km)
airQuality: AirQuality; // 空气质量
airQualityIndex: number; // 空气质量指数AQI
sunrise: string; // 日出时间
sunset: string; // 日落时间
updateTime: number; // 更新时间戳
}
/**
* 天气类型枚举
*/
export enum WeatherType {
SUNNY = 'sunny', // 晴天
CLOUDY = 'cloudy', // 多云
OVERCAST = 'overcast', // 阴天
RAINY = 'rainy', // 雨天
HEAVY_RAIN = 'heavy_rain', // 大雨
THUNDERSTORM = 'thunderstorm', // 雷阵雨
SNOWY = 'snowy', // 雪天
FOGGY = 'foggy' // 雾天
}
天气预报接口:
typescript
/**
* 每日天气预报
*/
export interface DailyForecast {
date: number; // 日期时间戳
dateStr: string; // 日期字符串
weatherType: WeatherType; // 天气类型
weatherDesc: string; // 天气描述
tempMax: number; // 最高温度
tempMin: number; // 最低温度
humidity: number; // 湿度
rainfall: number; // 降雨量(mm)
windSpeed: number; // 风速
uvIndex: number; // 紫外线指数
}
/**
* 每小时天气预报
*/
export interface HourlyForecast {
time: number; // 时间戳
timeStr: string; // 时间字符串
temperature: number; // 温度
weatherType: WeatherType; // 天气类型
weatherDesc: string; // 天气描述
rainfall: number; // 降雨概率(%)
windSpeed: number; // 风速
}
二、高德地图Weather API集成
2.1 导入Weather API
在页面中导入高德地图搜索SDK的天气相关类:
typescript
import {
WeatherSearch, // 天气搜索
WeatherSearchQuery, // 天气查询
LocalWeatherLive, // 实时天气
LocalWeatherLiveResult, // 实时天气结果
OnWeatherSearchListener, // 天气搜索监听器
AMapException // 异常码
} from '@amap/amap_lbs_search';
2.2 实现天气查询
在FieldMapPage中实现天气查询:
typescript
/**
* 查询当前城市天气
* @param cityName 城市名称(如:武汉市)
*/
private searchWeather(cityName: string): void {
if (!cityName) {
console.warn('[FieldMapPage] 城市名称为空,无法查询天气');
return;
}
try {
const context = getContext(this) as Context;
// 1. 创建天气搜索监听器
const weatherSearchListener: OnWeatherSearchListener = {
// 实时天气查询回调
onWeatherLiveSearched: (
weatherLiveResult: LocalWeatherLiveResult | undefined,
errorCode: number
): void => {
if (errorCode === AMapException.CODE_AMAP_SUCCESS) {
if (weatherLiveResult && weatherLiveResult.getLiveResult()) {
const liveWeather: LocalWeatherLive = weatherLiveResult.getLiveResult();
// 提取天气信息
this.currentWeather = liveWeather.getWeather() || '';
this.currentTemperature = liveWeather.getTemperature() || '';
this.windDirection = liveWeather.getWindDirection() || '';
this.windPower = liveWeather.getWindPower() || '';
this.humidity = liveWeather.getHumidity() || '';
this.reportTime = liveWeather.getReportTime() || '';
console.info(`[FieldMapPage] ✅ 天气查询成功: ${this.currentWeather} ${this.currentTemperature}°C`);
// 可选:语音播报天气
this.speakWeatherInfo();
}
} else {
console.error(`[FieldMapPage] ❌ 天气查询失败,错误码: ${errorCode}`);
promptAction.showToast({
message: '天气查询失败',
duration: 2000
});
}
},
// 天气预报查询回调(本例不使用)
onWeatherForecastSearched: (): void => {
// 不处理预报天气
}
};
// 2. 创建天气查询
const query = new WeatherSearchQuery(
cityName, // 城市名称
WeatherSearchQuery.WEATHER_TYPE_LIVE // 查询类型:实时天气
);
// 3. 创建天气搜索对象
const weatherSearch = new WeatherSearch(context);
weatherSearch.setOnWeatherSearchListener(weatherSearchListener);
weatherSearch.setQuery(query);
// 4. 执行异步查询
weatherSearch.searchWeatherAsyn();
console.info(`[FieldMapPage] 🔍 开始查询天气: ${cityName}`);
} catch (error) {
console.error('[FieldMapPage] 天气查询异常:', error);
promptAction.showToast({
message: '天气服务异常',
duration: 2000
});
}
}
关键步骤:
- 创建
OnWeatherSearchListener监听器 - 创建
WeatherSearchQuery查询对象 - 创建
WeatherSearch搜索对象 - 设置监听器和查询参数
- 调用
searchWeatherAsyn()执行异步查询
2.3 获取城市名称
通过逆地理编码获取当前位置的城市名:
typescript
/**
* 获取定位城市信息并查询天气
* @param latitude 纬度
* @param longitude 经度
*/
private async getLocationCity(latitude: number, longitude: number): Promise<void> {
try {
// 1. 创建逆地理编码请求
const reverseGeoCodeRequest: geoLocationManager.ReverseGeoCodeRequest = {
latitude: latitude,
longitude: longitude,
maxItems: 1,
locale: 'zh'
};
// 2. 执行逆地理编码
geoLocationManager.getAddressesFromLocation(reverseGeoCodeRequest)
.then((addresses) => {
if (addresses && addresses.length > 0) {
const geoAddress = addresses[0];
// 3. 提取城市信息
const city = geoAddress.locality || geoAddress.subAdministrativeArea || '';
const district = geoAddress.subLocality || '';
const province = geoAddress.administrativeArea || '';
console.info(`[FieldMapPage] 逆地理编码成功: ${province} ${city} ${district}`);
// 4. 查询天气
if (city) {
this.searchWeather(city);
}
}
})
.catch((error: BusinessError) => {
console.error('[FieldMapPage] 逆地理编码失败:', error.message);
});
} catch (error) {
console.error('[FieldMapPage] 逆地理编码异常:', error);
}
}
使用场景:
- 在定位成功后调用,自动查询当前位置的天气
- 用户移动到新位置时更新天气信息
2.4 天气图标映射
根据天气描述返回对应的emoji图标:
typescript
/**
* 根据天气描述返回对应图标
* @param weather 天气描述
* @returns emoji图标
*/
private getWeatherIcon(weather: string): string {
if (weather.includes('晴')) return '☀️';
if (weather.includes('云') || weather.includes('阴')) return '☁️';
if (weather.includes('雨')) return '🌧️';
if (weather.includes('雪')) return '❄️';
if (weather.includes('雾') || weather.includes('霾')) return '🌫️';
if (weather.includes('雷')) return '⛈️';
if (weather.includes('风')) return '💨';
return '🌤️'; // 默认图标
}
三、天气展示UI实现
3.1 地图页天气卡片
在地图页面右上角显示天气信息:
typescript
// 在FieldMapPage的build方法中
if (this.currentWeather) {
Row() {
// 天气图标
Text(this.getWeatherIcon(this.currentWeather))
.fontSize(24)
// 天气信息
Column() {
Text(`${this.currentWeather} ${this.currentTemperature}°C`)
.fontSize(14)
.fontWeight(FontWeight.Medium)
.fontColor('#333333')
Text(`${this.windDirection}风 ${this.windPower}级`)
.fontSize(11)
.fontColor('#999999')
.margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start)
.margin({ left: 8 })
}
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.shadow({ radius: 4, color: '#22000000' })
.margin({ top: 80, right: 16 })
.onClick(() => {
// 点击显示天气详情
this.showWeatherDialog = true;
})
}
3.2 天气详情对话框
typescript
// 天气详情对话框
if (this.showWeatherDialog) {
Column() {
// 标题栏
Row() {
Text('天气详情')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
Blank()
Image($r('sys.media.ohos_ic_public_cancel'))
.width(24)
.height(24)
.fillColor('#666666')
.onClick(() => {
this.showWeatherDialog = false;
})
}
.width('100%')
.padding(16)
Divider()
// 天气信息
Column({ space: 16 }) {
// 主要信息
Row() {
Text(this.getWeatherIcon(this.currentWeather))
.fontSize(64)
Column() {
Text(`${this.currentTemperature}°C`)
.fontSize(48)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
Text(this.currentWeather)
.fontSize(16)
.fontColor('#666666')
}
.alignItems(HorizontalAlign.Start)
.margin({ left: 24 })
}
.width('100%')
.justifyContent(FlexAlign.Center)
.padding({ top: 24, bottom: 24 })
// 详细信息
Column({ space: 12 }) {
this.buildWeatherInfoRow('风向风力', `${this.windDirection}风 ${this.windPower}级`);
this.buildWeatherInfoRow('湿度', `${this.humidity}%`);
this.buildWeatherInfoRow('更新时间', this.reportTime);
}
.width('100%')
.padding(16)
.backgroundColor('#F5F5F5')
.borderRadius(12)
// 农事建议
this.buildAgricultureAdvice();
}
.width('100%')
.padding(16)
}
.width('90%')
.backgroundColor('#FFFFFF')
.borderRadius(16)
.shadow({ radius: 16, color: '#33000000' })
}
/**
* 构建天气信息行
*/
@Builder
buildWeatherInfoRow(label: string, value: string) {
Row() {
Text(label)
.fontSize(14)
.fontColor('#666666')
.width(80)
Text(value)
.fontSize(14)
.fontColor('#333333')
.fontWeight(FontWeight.Medium)
.layoutWeight(1)
}
.width('100%')
}
3.3 农事建议生成
根据天气条件生成农事建议:
typescript
/**
* 构建农事建议
*/
@Builder
buildAgricultureAdvice() {
Column({ space: 12 }) {
Text('🌾 农事建议')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.width('100%')
Column({ space: 8 }) {
ForEach(this.getAgricultureAdvice(), (advice: string, index: number) => {
Row({ space: 8 }) {
Text(`${index + 1}.`)
.fontSize(14)
.fontColor('#666666')
.width(24)
Text(advice)
.fontSize(14)
.fontColor('#333333')
.lineHeight(22)
.layoutWeight(1)
}
.width('100%')
}, (advice: string, index: number) => `advice_${index}`)
}
.width('100%')
.padding(12)
.backgroundColor('#FFF9E6')
.borderRadius(8)
}
.width('100%')
}
/**
* 根据天气生成农事建议
*/
private getAgricultureAdvice(): string[] {
const advice: string[] = [];
const weather = this.currentWeather;
const temp = parseInt(this.currentTemperature);
const humidity = parseInt(this.humidity);
// 根据天气类型给建议
if (weather.includes('晴')) {
advice.push('天气晴朗,适合进行田间作业');
if (temp > 30) {
advice.push('温度较高,注意及时浇水,避免作物缺水');
advice.push('高温天气,建议早晚进行农事活动');
} else if (temp > 20) {
advice.push('温度适宜,可以进行播种、移栽等工作');
}
} else if (weather.includes('雨')) {
advice.push('雨天不宜进行田间作业');
advice.push('注意排水,防止田间积水');
advice.push('雨后注意病虫害防治');
} else if (weather.includes('云') || weather.includes('阴')) {
advice.push('多云天气,适合进行大部分农事活动');
advice.push('光照较弱,注意观察作物生长情况');
}
// 根据湿度给建议
if (humidity > 80) {
advice.push('湿度较高,注意通风,预防病害发生');
} else if (humidity < 40) {
advice.push('空气干燥,注意增加灌溉频次');
}
// 根据风力给建议
const windLevel = parseInt(this.windPower);
if (windLevel >= 5) {
advice.push('风力较大,不宜喷洒农药,避免药液飘移');
}
return advice.length > 0 ? advice : ['当前天气条件良好,可正常进行农事活动'];
}
四、天气数据缓存
4.1 缓存策略设计
为了减少API调用和提升性能,实现天气数据缓存:
typescript
/**
* 天气缓存接口
*/
interface WeatherCache {
weather: string;
temperature: string;
windDirection: string;
windPower: string;
humidity: string;
reportTime: string;
cacheTime: number; // 缓存时间戳
}
/**
* 缓存天气数据
*/
private async cacheWeatherData(): Promise<void> {
const cache: WeatherCache = {
weather: this.currentWeather,
temperature: this.currentTemperature,
windDirection: this.windDirection,
windPower: this.windPower,
humidity: this.humidity,
reportTime: this.reportTime,
cacheTime: Date.now()
};
await StorageUtil.saveObject('weather_cache', cache);
console.info('[FieldMapPage] 天气数据已缓存');
}
/**
* 读取缓存的天气数据
*/
private async loadCachedWeather(): Promise<boolean> {
try {
const cache = await StorageUtil.getObject<WeatherCache>('weather_cache', null);
if (!cache) {
return false;
}
// 检查缓存是否过期(30分钟)
const cacheAge = Date.now() - cache.cacheTime;
const CACHE_DURATION = 30 * 60 * 1000; // 30分钟
if (cacheAge > CACHE_DURATION) {
console.info('[FieldMapPage] 天气缓存已过期');
return false;
}
// 使用缓存数据
this.currentWeather = cache.weather;
this.currentTemperature = cache.temperature;
this.windDirection = cache.windDirection;
this.windPower = cache.windPower;
this.humidity = cache.humidity;
this.reportTime = cache.reportTime;
console.info('[FieldMapPage] 使用缓存的天气数据');
return true;
} catch (error) {
console.error('[FieldMapPage] 读取天气缓存失败:', error);
return false;
}
}
缓存策略:
- 缓存有效期:30分钟
- 优先使用缓存数据
- 缓存过期后重新查询
- 查询成功后更新缓存
五、语音播报天气
结合TTS服务播报天气信息:
typescript
/**
* 语音播报天气信息
*/
private async speakWeatherInfo(): Promise<void> {
try {
const text = `当前天气${this.currentWeather},温度${this.currentTemperature}度,` +
`${this.windDirection}风${this.windPower}级,湿度${this.humidity}%`;
await ttsService.speak(text, 1.0, 0.9);
console.info('[FieldMapPage] 天气信息已播报');
} catch (error) {
console.error('[FieldMapPage] 天气播报失败:', error);
}
}
六、实操练习
练习1:查看实时天气
任务:在地图页面查看当前位置的天气
步骤:
- 运行应用,进入地图页面
- 确保定位权限已授权
- 等待定位成功
- 观察右上角的天气卡片
- 点击天气卡片查看详情
预期结果:
- 显示当前天气、温度
- 显示风向、风力、湿度
- 显示农事建议
练习2:测试天气缓存
任务:验证天气数据缓存功能
步骤:
- 首次打开应用,查询天气
- 关闭应用
- 30分钟内重新打开应用
- 观察控制台日志
预期结果:
- 首次查询调用API
- 30分钟内使用缓存数据
- 30分钟后重新查询API
练习3:根据天气调整建议
任务:修改农事建议逻辑
代码:
typescript
private getAgricultureAdvice(): string[] {
const advice: string[] = [];
// 添加自定义建议逻辑
if (this.currentWeather.includes('雨')) {
advice.push('雨天适合室内整理农具');
advice.push('检查排水系统是否正常');
}
return advice;
}
七、总结
本篇教程完成了天气服务的集成,主要包括:
✅ 已实现功能
| 功能 | 说明 |
|---|---|
| 实时天气查询 | 基于高德Weather API |
| 逆地理编码 | 获取当前位置城市名 |
| 天气展示 | 地图页卡片 + 详情对话框 |
| 农事建议 | 根据天气生成建议 |
| 数据缓存 | 30分钟缓存策略 |
| 语音播报 | TTS播报天气信息 |
🎯 核心技术点
- WeatherSearch API:高德天气查询
- 逆地理编码:坐标转城市名
- 数据缓存:Preferences存储
- 智能建议:天气与农事关联
- UI设计:天气卡片与详情页
🚀 下一步
在下一篇教程中,我们将学习二十四节气与农事指导系统。
恭喜! 🎉 你已经完成了天气服务的集成,为智慧农业管理增添了重要的气象数据支持。