🎉 init(init):初始化仓库

This commit is contained in:
张鑫
2025-12-23 08:38:08 +08:00
parent 36226cc9fe
commit 066fe58f89
34 changed files with 3402 additions and 2 deletions

0
irrgiation/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,68 @@
crop_growth_stages={
'甘草': {
'initial': {
'root_length': {5: 0.8, 10: 0.2}, # 主根长80%概率在0-15cm平均约8cm
'duration_days': 20, # 6月播种后1-2周
'kc': 0.40, # 作物系数
'time_range': '2025-07-08至2025-07-28',
'hight':0.03,#m
'k_rain':0.25
},
'development': {
'root_length': {20: 0.6, 45: 0.4}, # 主根长60%概率在20cm40%概率在45cm平均约30cm
'duration_days': 42, # 发芽后-8月20日左右约2个月
'kc': 0.9, # 作物系数
'time_range': '2025-07-29至2025-09-10',
'hight':0.15,
'k_rain':0.25
},
'mid': {
'root_length': {30: 0.5, 80: 0.5}, # 主根长50%概率在30cm50%概率在80cm平均约55cm
'duration_days': 30, # 8月20日-9月下旬
'kc': 1.1, # 作物系数
'time_range': '2025-09-11至2025-10-10',
'hight':0.45,
'soil_evaporation_depth': 0.65,
'k_rain':0.4
},
'growth_slowly': {
'root_length':{40: 0.4,60:0.4, 80: 0.2}, # 根据数据估算平均主根长
'duration_days': 31, # 9月下旬-10月下旬
'kc': 0.6,
'time_range': '2025-10-11至2025-11-10',
'hight':0.6
},
'dormant_period': {
'root_length': {40: 0.3,60:0.4, 80: 0.2}, # 冬季休眠,主根基本停止生长
'duration_days': 140, # 11月-次年3月
'kc': 0.4,
'time_range': '2025-11-11至2026-03-31',
'hight':0.8,
'k_rain':0.5
},
'fanqing_year2': {
'root_length': {40: 0.2,60:0.6, 80: 0.2}, # 返青期主根长变化不大
'duration_days': 30, # 3月下旬-4月下旬
'kc': 0.35,
'time_range': '2026-04-01至2026-04-30',
'hight':0.5,
'k_rain':0.5
},
'development_year2': {
'root_length': {40: 0.1,60:0.7, 80: 0.2}, # 根据数据估算平均主根长
'duration_days': 90, # 4月下旬-7月下旬
'kc': 0.9,
'time_range': '2026-05-01至2026-07-30',
'hight':0.45,
'k_rain':0.65
},
'maturity_year2': {
'root_length':{40: 0.1,60:0.6, 80: 0.3}, # 根据数据估算平均主根长
'duration_days': 40, # 7月下旬-8月下旬
'kc': 0.6,
'time_range': '2026-08-01至2026-09-10',
'hight':0.65,
'k_rain':0.5
}
}
}

View File

@@ -0,0 +1,365 @@
from datetime import datetime, timedelta
from pyeto import fao,convert
from pyeto.fao import svp_from_t, avp_from_rhmean, delta_svp, psy_const,atm_pressure
import pandas as pd
#1、计算日参考蒸散量
def calculateET0(tmean, tmax, tmin, rh_mean, wind_speed, solar_rad, elevation, lat, doy):
# 计算饱和水汽压和实际水汽压
svp_max = svp_from_t(tmax)
svp_min = svp_from_t(tmin)
svp_mean =(svp_max+svp_min)/2
avp = avp_from_rhmean(svp_max, svp_min, rh_mean)
# 计算净辐射(简化版)
# 将纬度从度转换为弧度
lat_rad = convert.deg2rad(lat)
sol_dec = fao.sol_dec(doy)
sha = fao.sunset_hour_angle(lat_rad, sol_dec)
ird = fao.inv_rel_dist_earth_sun(doy)
et_rad = fao.et_rad(lat_rad, sol_dec, sha, ird)
cs_rad = fao.cs_rad(elevation, et_rad)
nisr = fao.net_in_sol_rad(solar_rad)
nolr = fao.net_out_lw_rad(tmin, tmax, solar_rad, cs_rad, avp)
rn = fao.net_rad(nisr, nolr)
deltasvp= delta_svp(tmean)
psy = psy_const(atm_pressure(elevation))
# 计算ET₀
et0 = fao.fao56_penman_monteith(
net_rad=rn,
t=tmean,
ws=wind_speed,
svp=svp_mean,
avp=avp,
delta_svp=deltasvp,
psy=psy
)
return et0
def cal_et0_List(planting_date,monitor_date,date_params):
et0_list = []
current_date = planting_date
while current_date < monitor_date:
result_params=date_params[current_date.strftime("%Y-%m-%d")]
et0=calculateET0(result_params['tmean'], result_params['tmax'], result_params['tmin'], result_params['rh_mean'], result_params['wind_speed'],
result_params['solar_rad'], result_params['elevation'],result_params['lat'], result_params['doy'])
et0_list.append(et0)
current_date += timedelta(days=1) # 增加一天
return et0_list
#计算实际每天作物蒸散量
# 1. 定义甘草生育阶段与作物系数(参考苜蓿)
# growth_stages = {
# "initial": {"duration_days": 20, "kc": 0.40}, # 生长初期
# "development": {"duration_days": 40, "kc": 0.90}, #生长快速期
# "mid": {"duration_days": 20, "kc": 1.1}, # 生长缓慢期
# "growth_slowly": {"duration_days": 30, "kc": 0.6}, # 生长缓慢期
# "dormant_period": {"duration_days": 150, "kc": 0.40}, # 营养储备期
# "fanqing_year2":{"duration_days": 30, "kc": 0.35},#返青期
# "development_year2":{"duration_days": 90, "kc": 0.9}, #第二年快速生长期
# "maturity _year2": {"duration_days": 40, "kc": 0.6}, # 第二年成熟期
# }
growth_stages_roots1 = {
'initial': {
'root_length': {5: 0.8, 10: 0.2}, # 主根长80%概率在0-15cm平均约8cm
'duration_days': 23, # 6月播种后1-2周
'kc': 0.40, # 作物系数
'time_range': '2025-06-17至2025-07-10',
'hight':0.03,#m
'k_rain':0.25
},
'development': {
'root_length': {20: 0.6, 45: 0.4}, # 主根长60%概率在20cm40%概率在45cm平均约30cm
'duration_days': 40, # 发芽后-8月20日左右约2个月
'kc': 0.9, # 作物系数
'time_range': '2025-07-10至2025-08-20',
'hight':0.15,
'k_rain':0.25
},
'mid': {
'root_length': {30: 0.5, 80: 0.5}, # 主根长50%概率在30cm50%概率在80cm平均约55cm
'duration_days': 30, # 8月20日-9月下旬
'kc': 1.1, # 作物系数
'time_range': '2025-08-21至2025-09-20',
'hight':0.45,
'soil_evaporation_depth': 0.65,
'k_rain':0.4
},
'growth_slowly': {
'root_length':{40: 0.4,60:0.4, 80: 0.2}, # 根据数据估算平均主根长
'duration_days': 30, # 9月下旬-10月下旬
'kc': 0.6,
'time_range': '2025-09-21至2025-10-20',
'hight':0.6
},
'dormant_period': {
'root_length': {40: 0.3,60:0.4, 80: 0.2}, # 冬季休眠,主根基本停止生长
'duration_days': 160, # 11月-次年3月
'kc': 0.4,
'time_range': '2025-10-21至2026-03-30',
'hight':0.8,
'k_rain':0.5
},
'fanqing_year2': {
'root_length': {40: 0.2,60:0.6, 80: 0.2}, # 返青期主根长变化不大
'duration_days': 30, # 3月下旬-4月下旬
'kc': 0.35,
'time_range': '2026-04-01至2026-04-30',
'hight':0.5,
'k_rain':0.5
},
'development_year2': {
'root_length': {40: 0.1,60:0.7, 80: 0.2}, # 根据数据估算平均主根长
'duration_days': 90, # 4月下旬-7月下旬
'kc': 0.9,
'time_range': '2026-05-01至2026-07-30',
'hight':0.45,
'k_rain':0.65
},
'maturity_year2': {
'root_length':{40: 0.1,60:0.6, 80: 0.3}, # 根据数据估算平均主根长
'duration_days': 40, # 7月下旬-8月下旬
'kc': 0.6,
'time_range': '2026-08-01至2026-09-10',
'hight':0.65,
'k_rain':0.5
}
}
# growth_stages_roots = {
# 'initial': {
# 'root_length': {5: 0.8, 10: 0.2}, # 主根长80%概率在0-15cm平均约8cm
# 'duration_days': 20, # 6月播种后1-2周
# 'kc': 0.40, # 作物系数
# 'time_range': '2025-07-08至2025-07-28',
# 'hight':0.03,#m
# 'k_rain':0.25
# },
# 'development': {
# 'root_length': {20: 0.6, 45: 0.4}, # 主根长60%概率在20cm40%概率在45cm平均约30cm
# 'duration_days': 42, # 发芽后-8月20日左右约2个月
# 'kc': 0.9, # 作物系数
# 'time_range': '2025-07-29至2025-09-10',
# 'hight':0.15,
# 'k_rain':0.25
# },
# 'mid': {
# 'root_length': {30: 0.5, 80: 0.5}, # 主根长50%概率在30cm50%概率在80cm平均约55cm
# 'duration_days': 30, # 8月20日-9月下旬
# 'kc': 1.1, # 作物系数
# 'time_range': '2025-09-11至2025-10-10',
# 'hight':0.45,
# 'soil_evaporation_depth': 0.65,
# 'k_rain':0.4
# },
# 'growth_slowly': {
# 'root_length':{40: 0.4,60:0.4, 80: 0.2}, # 根据数据估算平均主根长
# 'duration_days': 31, # 9月下旬-10月下旬
# 'kc': 0.6,
# 'time_range': '2025-10-11至2025-11-10',
# 'hight':0.6
# },
# 'dormant_period': {
# 'root_length': {40: 0.3,60:0.4, 80: 0.2}, # 冬季休眠,主根基本停止生长
# 'duration_days': 140, # 11月-次年3月
# 'kc': 0.4,
# 'time_range': '2025-11-11至2026-03-31',
# 'hight':0.8,
# 'k_rain':0.5
# },
# 'fanqing_year2': {
# 'root_length': {40: 0.2,60:0.6, 80: 0.2}, # 返青期主根长变化不大
# 'duration_days': 30, # 3月下旬-4月下旬
# 'kc': 0.35,
# 'time_range': '2026-04-01至2026-04-30',
# 'hight':0.5,
# 'k_rain':0.5
# },
# 'development_year2': {
# 'root_length': {40: 0.1,60:0.7, 80: 0.2}, # 根据数据估算平均主根长
# 'duration_days': 90, # 4月下旬-7月下旬
# 'kc': 0.9,
# 'time_range': '2026-05-01至2026-07-30',
# 'hight':0.45,
# 'k_rain':0.65
# },
# 'maturity_year2': {
# 'root_length':{40: 0.1,60:0.6, 80: 0.3}, # 根据数据估算平均主根长
# 'duration_days': 40, # 7月下旬-8月下旬
# 'kc': 0.6,
# 'time_range': '2026-08-01至2026-09-10',
# 'hight':0.65,
# 'k_rain':0.5
# }
# }
# ---------------------------
# 2. 计算每日Kc值根据生育阶段动态分配
# ---------------------------
def get_daily_kc(growth_stages, total_days=423):
daily_kc = []
for stage in growth_stages.values():
daily_kc.extend([stage["kc"]] * stage["duration_days"])
# 如果总天数超出定义用末期Kc填充剩余天数
if len(daily_kc) < total_days:
daily_kc.extend([growth_stages["maturity_year2"]["kc"]] * (total_days - len(daily_kc)))
return daily_kc[:total_days] # 截断至总天数
# ---------------------------
# 3. 计算每日实际蒸散量ETc(得到历史列表)
# ---------------------------
def calculate_etc_List(et0_daily, planting_date,monitor_date, kc_daily):
"""
计算每日ETc实际蒸散量
:param eto_daily: 每日参考蒸散量列表单位cm/d
:param planting_date: 播种日期,格式"YYYY-MM-DD"
:param kc_daily: 每日Kc值列表
:return: 日期列表、ETc列表
"""
start_date = planting_date
delta=monitor_date-start_date
days = delta.days
dates = [start_date + timedelta(days=i) for i in range(days)]
date_strings = [date.strftime("%Y-%m-%d") for date in dates]
kc_daily = kc_daily[:days]
etc = [eto * kc for eto, kc in zip(et0_daily, kc_daily)]
return etc,date_strings,kc_daily
#基于作物蒸散和土壤蒸发计算实际蒸散量
def calculate_etc_List_new(et0_daily, planting_date,monitor_date, kc_daily,ke_daily):
"""
计算每日ETc实际蒸散量
:param eto_daily: 每日参考蒸散量列表单位cm/d
:param planting_date: 播种日期,格式"YYYY-MM-DD"
:param kc_daily: 每日Kc值列表
:return: 日期列表、ETc列表
"""
start_date = planting_date
delta=monitor_date-start_date
days = delta.days
dates = [start_date + timedelta(days=i) for i in range(days)]
date_strings = [date.strftime("%Y-%m-%d") for date in dates]
kc_daily = kc_daily[:days]
etc = [eto * (kc+ke) for eto, kc,ke in zip(et0_daily, kc_daily,ke_daily)]
return etc,date_strings,kc_daily
def calculate_etc_dayily(et0_daily, planting_date,monitor_date, kc_daily):
"""
计算每日ETc实际蒸散量
:param eto_daily: 每日参考蒸散量列表单位cm/d
:param planting_date: 播种日期,格式"YYYY-MM-DD"
:param kc_daily: 每日Kc值列表
:return: 日期列表、ETc列表
"""
start_date = planting_date
delta=monitor_date-start_date
days = delta.days
# dates = [start_date + timedelta(days=i) for i in range(len(kc_daily))]
kc_daily = kc_daily[days]
etc = et0_daily*kc_daily
return etc
def export_etc_et0_date(et0_list,etc_list,dates,kc_list,ke_daily,soil_data,rain_daily,irrigate_daily,dk_name):
df = pd.DataFrame({
"Date": dates,
"ET0": [float(f"{x:.3f}") for x in et0_list],
"KC": kc_list,
"KE":ke_daily,
"ETc": [float(f"{x:.3f}") for x in etc_list],
"rain_effective":rain_daily,
"I_effective":irrigate_daily,
"VWC_fc":0.2884,
"VWC_wp":0.1086,
"dkbm":dk_name
})
df['VWC_SUR'] = 0.0
df['VWC_MID'] = 0.0
df['VWC_BOT'] = 0.0
for i in range(len(df)):
date=df.at[i, 'Date']
df.at[i, 'VWC_SUR']=round(float(soil_data["SOIL_MOISTURE_SURFACE"][date]),3)
df.at[i, 'VWC_MID'] = round(float(soil_data["SOIL_MOISTURE_MIDDLE"][date]),3)
df.at[i, 'VWC_BOT'] = round(float(soil_data["SOIL_MOISTURE_BOTTOM"][date]),3)
return df
def estimate_mad_from_et(max_et):
# 根据最大日蒸散量查表确定MAD值
if max_et < 2:
mad = 0.68
elif 2 <= max_et < 3:
mad = 0.58
elif 3 <= max_et < 4:
mad = 0.48
elif 4 <= max_et < 5:
mad = 0.4
elif 5 <= max_et < 6:
mad = 0.35
elif 6 <= max_et < 7:
mad = 0.33
elif 7 <= max_et < 8:
mad = 0.28
elif 8 <= max_et < 9:
mad = 0.25
else: # max_et >= 8
mad = 0.23
return mad
def calculate_weighted_soil_parameters(root_distribution, soil_layers):
# 初始化累计变量
sum_fc = 0.0
sum_wp = 0.0
# 处理每个根系分布层
for root_depth, root_ratio in root_distribution['root_length'].items():
# 找到包含该深度的土壤层
for layer in soil_layers:
min_depth, max_depth = layer['depth_range']
if min_depth <= root_depth < max_depth:
# 计算该层贡献 (简单比例分配)
layer_thickness = max_depth - min_depth
depth_in_layer = min(root_depth, max_depth) - min_depth
contribution_ratio = depth_in_layer / layer_thickness * root_ratio
sum_fc += layer['theta_33'] * contribution_ratio
sum_wp += layer['theta_1500'] * contribution_ratio
break
return sum_fc, sum_wp
def cal_stagebyDate(monitor_date,zwlx_growth_stages):
for stage, details in zwlx_growth_stages.items():
time_range_str = details['time_range']
if type(monitor_date) is str:
monitor_date= datetime.strptime(monitor_date, '%Y-%m-%d')
if '-' in time_range_str:
start_str, end_str = time_range_str.split('')
start_date = datetime.strptime(start_str, '%Y-%m-%d')
end_date = datetime.strptime(end_str, '%Y-%m-%d')
if start_date <= monitor_date <= end_date:
return stage,details['root_length'],details['hight'],details['k_rain']
raise ValueError(f"监测日期 {monitor_date.strftime('%Y-%m-%d')} 不在任何生育阶段范围内")
#根据日期计算主根长度
def calculate_average_root_length(root_length_dist):
return sum(length * prob for length, prob in root_length_dist.items())
def cal_soil_fc_wp():
# 土壤分层数据 (33kPa和1500kPa对应的含水率)
soil_data = [
{'depth_range': (0, 5), 'theta_33': 0.296, 'theta_1500': 0.103},
{'depth_range': (5, 15), 'theta_33': 0.284, 'theta_1500': 0.104},
{'depth_range': (15, 30), 'theta_33': 0.286, 'theta_1500': 0.105},
{'depth_range': (30, 60), 'theta_33': 0.290, 'theta_1500': 0.114},
{'depth_range': (60, 100), 'theta_33': 0.286, 'theta_1500': 0.117},
{'depth_range': (100, 200), 'theta_33': 0.284, 'theta_1500': 0.114}
]
# theta_fc, theta_wp = calculate_weighted_soil_parameters(
# growth_stages_roots[stage],
# soil_data
# )
theta_fc=0.2884
theta_wp=0.1086
return theta_fc,theta_wp
if __name__ == "__main__":
# #cal_et0_List(datetime(2026, 6, 18),datetime(2026, 6, 22))
et0_daily=[23,45,67,89]
# etc,dates,kc_daily=calculate_etc_List(et0_daily, datetime(2026, 6, 18),datetime(2026, 6, 22), get_daily_kc(growth_stages_roots))
# calculate_etc_dayily(45, datetime(2026, 6, 18), datetime(2026, 6, 22), get_daily_kc(growth_stages))
# df=export_etc_et0_date(et0_daily,etc,dates,kc_daily)
# print(df)
# cal_soil_fc_wp('initial')
# stage,details = cal_stagebyDate(datetime(2025, 7, 18))
# print(details)
# rd=calculate_average_root_length(details)
# print(rd)

199
irrgiation/db_connect.py Normal file
View File

@@ -0,0 +1,199 @@
import configparser
import os
from pathlib import Path
from typing import Optional, Union, List
import pandas as pd
import psycopg2
from dotenv import load_dotenv
from psycopg2 import sql
from psycopg2.extras import execute_batch
from sqlalchemy import create_engine, text
def get_config():
config = configparser.ConfigParser()
try:
project_nifi = Path(__file__).parent.parent / 'config.nifi'
config.read(project_nifi)
# 确保所有必要键都存在
required_keys = ['host', 'port', 'database', 'user', 'password','schema']
for key in required_keys:
if key not in config['postgresql']:
raise ValueError(f"Missing required config key: {key}")
return {
"host": config['postgresql']['host'],
"port": config['postgresql']['port'],
"database": config['postgresql']['database'],
"user": config['postgresql']['user'],
"password": config['postgresql']['password'],
"schema": config['postgresql']['schema'],
"tablename": config['postgresql']['tablename']
}
except Exception as e:
print(f"配置读取错误: {e}")
# 返回默认配置或退出
return None
def connect():
"""建立数据库连接"""
try:
config_params=get_config()
conn = psycopg2.connect(host=config_params['host'],port=config_params['port'],dbname=config_params['database'],
user=config_params['user'],password=config_params['password']
)
cursor = conn.cursor()
except Exception as e:
print(f"❌ 连接失败: {e}")
raise
# 根据条件查询
def query_postgresql_to_dataframe(
condition: Optional[dict] = None,
condition_operator: str = "AND",
columns: Union[str, List[str]] = "*",):
# 连接数据库
config_params = get_config()
conn = psycopg2.connect(host=config_params['host'], port=config_params['port'], dbname=config_params['database'],
user=config_params['user'], password=config_params['password']
)
try:
# 构建列选择部分
if isinstance(columns, list):
columns_sql = sql.SQL(", ").join(sql.Identifier(col) for col in columns)
else:
columns_sql = sql.SQL(columns)
# 构建基础查询
query = sql.SQL("SELECT {} FROM {}").format(
columns_sql,
sql.Identifier(config_params['tablename'])
)
# 构建条件部分(参数化)
params = {}
if condition:
conditions = []
for i, (key, value) in enumerate(condition.items()):
param_name = f"param_{i}"
conditions.append(sql.SQL("{} = %({})s").format(
sql.Identifier(key),
sql.SQL(param_name)
))
params[param_name] = value
where_clause = sql.SQL(" WHERE {}").format(
sql.SQL(f" {condition_operator} ").join(conditions)
)
query = query + where_clause
print(query)
# 执行查询
with conn.cursor() as cursor:
cursor.execute(query, params)
if cursor.description:
columns = [desc[0] for desc in cursor.description]
data = cursor.fetchall()
return pd.DataFrame(data, columns=columns)
return pd.DataFrame()
finally:
conn.close()
# 插入库表
def dataframe_to_postgresql_batch(df,batch_size=1000):
"""
使用execute_batch批量插入DataFrame数据
参数:
df: 要插入的DataFrame
table_name: 目标表名
config_params: 数据库连接配置
batch_size: 每批插入的行数
"""
if df.empty:
print("DataFrame为空无需插入")
return
# 获取列名
columns = df.columns.tolist()
data = [tuple(x) for x in df.to_numpy()]
try:
config_params = get_config()
conn = psycopg2.connect(host=config_params['host'], port=config_params['port'],
dbname=config_params['database'],
user=config_params['user'], password=config_params['password']
)
cursor = conn.cursor()
# 构建INSERT语句
insert_query = sql.SQL("INSERT INTO {} ({}) VALUES ({})").format(
sql.Identifier(config_params['tablename']),
sql.SQL(', ').join(map(sql.Identifier, columns)),
sql.SQL(', ').join([sql.Placeholder()] * len(columns))
)
# 批量执行
execute_batch(cursor, insert_query, data, batch_size)
conn.commit()
except Exception as e:
conn.rollback()
print(f"批量插入时出错: {e}")
finally:
if conn is not None:
conn.close()
# 根据条件更新某个字段
def update_irrigation_data(date_value, id_value, field_to_update, new_value):
"""
更新表中单条记录的单个字段
参数:
db_url: 数据库连接字符串
table_name: 表名
date_field: 日期字段名
id_field: ID/地块字段名
date_value: 日期值
id_value: ID/地块值
field_to_update: 要更新的字段名
new_value: 新值
"""
try:
# 数据库配置
db_config = {
"db_url": "postgresql://postgres:postgres@localhost:5432/datastore",
"table_name": "irrigation_data",
"date_field": "Date",
"id_field": "dkbm"
}
engine = create_engine(db_config["db_url"])
table_name=db_config["table_name"]
date_field=db_config["date_field"]
id_field=db_config["id_field"]
with engine.begin() as conn: # 自动提交事务
# 使用参数化查询防止SQL注入
update_sql = text(f"""
UPDATE {table_name}
SET "{field_to_update}" = :new_value
WHERE "{date_field}" = :date_value AND "{id_field}" = :id_value""")
conn.execute(update_sql, {
'new_value': new_value,
'date_value': date_value,
'id_value': id_value
})
return True
except Exception as e:
print(f"更新失败: {str(e)}")
return False
finally:
engine.dispose() # 确保连接关闭
if __name__ == '__main__':
config = configparser.ConfigParser()
project_nifi = Path(__file__).parent.parent / 'config.nifi'
config.read(project_nifi)

View File

@@ -0,0 +1,620 @@
import statistics
import uuid
from collections import defaultdict
from datetime import datetime, timedelta
import pandas as pd
from irrgiation.db_connect import query_postgresql_to_dataframe, dataframe_to_postgresql_batch,update_irrigation_data
from irrgiation.weatherAndSoilDataRequest import getWeatherAndSoilData, loginAuth, dateToTimestamp, get_soil_data_dk1, \
getFutureWeather, weather_params, get_soil_data_dk2
from irrgiation.dailyEvaporation import get_daily_kc, cal_et0_List, calculate_etc_List, export_etc_et0_date, \
cal_soil_fc_wp, estimate_mad_from_et, cal_stagebyDate, calculate_average_root_length,calculate_etc_List_new
from irrgiation.mathuntils import day_of_year
from irrgiation.soil_Ke import calculate_ke,calculate_D_ei
from irrgiation.crop_growth_stages import crop_growth_stages
#解析数据接口,计算均值、最大、最小值
def analy_soil_data(data):
soil_result = {}
for key in data:
daily_data = defaultdict(lambda: defaultdict(list))
for metric, entries in data[key].items():
for entry in entries:
dt = datetime.strptime(entry["datetime"], "%Y-%m-%d %H:%M:%S")
date_key = dt.date() # 按日期分组
value = float(entry["value"])
daily_data[date_key][metric].append(value)
result = {}
for date, metrics in daily_data.items():
date_stats = {}
for metric, values in metrics.items():
date_stats[metric] = {
"max": max(values),
"min": min(values),
"mean": statistics.mean(values),
'count': len(values),
'sum': sum(values)
}
result[str(date)] = date_stats # 将日期转为字符串作为键
soil_result[key] = result
return soil_result
#获取地块6个传感器的土壤湿度均值
def analy_soil_data_mean(soil_data):
result = defaultdict(lambda: {
"SOIL_MOISTURE_SURFACE": [],
"SOIL_MOISTURE_MIDDLE": [],
"SOIL_MOISTURE_BOTTOM": []
})
# 填充数据
for sensor, dates in soil_data.items():
for date, layers in dates.items():
result[date]["SOIL_MOISTURE_SURFACE"].append(layers["SOIL_MOISTURE_SURFACE"]["mean"])
result[date]["SOIL_MOISTURE_MIDDLE"].append(layers["SOIL_MOISTURE_MIDDLE"]["mean"])
result[date]["SOIL_MOISTURE_BOTTOM"].append(layers["SOIL_MOISTURE_BOTTOM"]["mean"])
# 转换为普通字典
final_result = dict(result)
result_soil = {}
for date, layers in final_result.items():
result_soil[date] = {
"SOIL_MOISTURE_SURFACE_MEAN": sum(layers["SOIL_MOISTURE_SURFACE"]) / len(layers["SOIL_MOISTURE_SURFACE"]),
"SOIL_MOISTURE_MIDDLE_MEAN": sum(layers["SOIL_MOISTURE_MIDDLE"]) / len(layers["SOIL_MOISTURE_MIDDLE"]),
"SOIL_MOISTURE_BOTTOM_MEAN": sum(layers["SOIL_MOISTURE_BOTTOM"]) / len(layers["SOIL_MOISTURE_BOTTOM"])
}
return final_result, result_soil
def analy_soil_dataNight_mean(soil_data):
daily_means = defaultdict(lambda: defaultdict(list))
# 计算平均值
for sensor, measurements in soil_data.items():
for measure_type, dates in measurements.items():
for date, value in dates.items():
daily_means[date][measure_type].append(float(value))
# 计算并格式化结果
result = {}
for date, measures in daily_means.items():
result[date] = {
f"{measure_type}_MEAN": round(sum(values) / len(values), 2)
for measure_type, values in measures.items()
}
return result
#获取地块6个传感器中土壤湿度最大值
def analy_soil_data_max(soil_data):
result = defaultdict(lambda: {
"SOIL_MOISTURE_SURFACE": [],
"SOIL_MOISTURE_MIDDLE": [],
"SOIL_MOISTURE_BOTTOM": []
})
# 填充数据
for sensor, dates in soil_data.items():
for date, layers in dates.items():
result[date]["SOIL_MOISTURE_SURFACE"].append(layers["SOIL_MOISTURE_SURFACE"]["mean"])
result[date]["SOIL_MOISTURE_MIDDLE"].append(layers["SOIL_MOISTURE_MIDDLE"]["mean"])
result[date]["SOIL_MOISTURE_BOTTOM"].append(layers["SOIL_MOISTURE_BOTTOM"]["mean"])
# 转换为普通字典
final_result = dict(result)
result_soil = {}
for date, layers in final_result.items():
result_soil[date] = {
"SOIL_MOISTURE_SURFACE_MEAN": max(layers["SOIL_MOISTURE_SURFACE"]),
"SOIL_MOISTURE_MIDDLE_MEAN": max(layers["SOIL_MOISTURE_MIDDLE"]),
"SOIL_MOISTURE_BOTTOM_MEAN": max(layers["SOIL_MOISTURE_BOTTOM"])
}
return final_result, result_soil
def get_soil_data_night_eyveryDay(data):
daily_data = {}
for sersor in data:
data_depth = {}
for key in data[sersor]:
target_time = (21, 59, 59)
result = {} # 时、分、秒元组
daily_records = {} # 临时存储每天的所有记录用于找不到22点数据时获取最新记录
for record in data[sersor][key]:
try:
dt = datetime.strptime(record["datetime"], "%Y-%m-%d %H:%M:%S")
date_str = record["datetime"].split(" ")[0] # 提取日期部分
# 严格匹配 02:00:00
# 提取时间部分(时、分、秒)
current_time = (dt.hour, dt.minute, dt.second)
# 存储每天的所有记录(按时间排序)
if date_str not in daily_records:
daily_records[date_str] = []
daily_records[date_str].append((dt, record['value']))
# 比较时间部分
if current_time >= target_time:
if date_str not in result:
result[date_str] = record['value']
except:
continue
# 对于没有22点数据的日期取当天最新的一条数据
for date_str in daily_records:
if date_str not in result and daily_records[date_str]:
daily_records[date_str].sort(key=lambda x: x[0]) # 按datetime排序
result[date_str] = daily_records[date_str][-1][1] # 取最后一个值
data_depth[key] = result
daily_data[sersor] = data_depth
return daily_data
def analyze_weather_data1(data):
# 1. 按日期分组数据
daily_data = defaultdict(lambda: defaultdict(list))
for metric, entries in data.items():
for entry in entries:
dt = datetime.strptime(entry["datetime"], "%Y-%m-%d %H:%M:%S")
date_key = dt.date() # 按日期分组
if metric == "AIR_LUX":
value = float(entry["value_solar"])
else:
value = float(entry["value"])
daily_data[date_key][metric].append(value)
# 2. 计算每日统计值
result = {}
for date, metrics in daily_data.items():
date_stats = {}
for metric, values in metrics.items():
date_stats[metric] = {
"max": max(values),
"min": min(values),
"mean": statistics.mean(values),
'count': len(values),
'sum': sum(values)
}
result[str(date)] = date_stats # 将日期转为字符串作为键
return result
# 获取彭曼公式的入参
def get_params(start_date, monitor_date, data):
delta = monitor_date - start_date
days = delta.days
dates = [start_date + timedelta(days=i) for i in range(days)]
date_strings = [date.strftime("%Y-%m-%d") for date in dates]
date_params = {}
for date in date_strings:
result_params = get_weather_params(data, date)
date_params[date] = result_params
return date_params
def get_weather_params(data, date):
result = {}
elevation = 865 # 海拔(m)
lat = 41.34252110189814 # 纬度(度)
#计算当前日期位于当年的多少天
doy = day_of_year(date)
for item in data['AIR_LUX']:
item['value_solar'] = float(item['value']) * 0.0081 * 3600
data_tj = analyze_weather_data1(data)
data_tj_date = data_tj[date];
tmax = data_tj_date['AIR_TEMPERATURE']['max']
tmin = data_tj_date['AIR_TEMPERATURE']['min']
tmean = data_tj_date['AIR_TEMPERATURE']['mean']
rh_mean = data_tj_date['AIR_HUMIDITY']['mean']
wind_speed = data_tj_date['WIND_SPEED']['mean']
solar = data_tj_date['AIR_LUX']['sum'] / 1000000
result['tmax'] = round(tmax, 3)
result['tmin'] = round(tmin, 3)
result['tmean'] = round(tmean, 3)
result['rh_mean'] = round(rh_mean, 3)
result['wind_speed'] = round(wind_speed, 3)*0.94
result['solar_rad'] = round(solar, 3)
result['elevation'] = elevation
result['lat'] = round(lat, 3)
result['doy'] = doy
return result
# 判断是否需要灌溉
def isNeedirrigate(df, qx_coff, soil_coff,Di_1,zwlx_growth_stages):
df['RD'] = 0.0
df['Di'] = 0.0
df['Di-1'] = Di_1
df['mad'] = 0.0
df['irrigation_depth'] = 0.0
df['irrigation_acre'] = 0.0
df['isNeedirrigate'] = False
df['Reset_Flag'] = False
# df['irrigate_sd_predict'] = 0.0
df['f_t'] = 0.0
df['f_js'] = 0.0
df['f_soil'] = 0.0
for i in range(len(df)):
# 设置Di-1前一天累计值
if i > 0:
df.at[i, 'Di-1'] = df.at[i - 1, 'Di']
# 计算当前累计值Di-1 + 当天ETc
raw, rd, mad, k_rain = cal_raw(df.at[i, 'ETc'], df.at[i, 'Date'],zwlx_growth_stages)
if df.at[i, 'rain_effective']<5:
df.at[i, 'rain_effective'] = 0
else:
df.at[i, 'rain_effective'] = df.at[i, 'rain_effective'] * k_rain
current_cum = df.at[i, 'Di-1'] + df.at[i, 'ETc']- df.at[i, 'rain_effective']
df['RD'] = rd
df.at[i,'mad'] = mad
df.at[i, 'raw'] = round(raw, 3)
f_t = qx_coff[df.at[i, 'Date']]['qw']
f_js = qx_coff[df.at[i, 'Date']]['js']
f_soil = soil_coff[df.at[i, 'Date']]
raw_adj = cal_raw_adjust(raw, f_t, f_js, f_soil)
df.at[i, 'f_t'] = f_t
df.at[i, 'f_js'] = f_js
df.at[i, 'f_soil'] = f_soil
df.at[i, 'raw_adjust'] = round(raw_adj, 3)
# 判断是否需要重置
if current_cum > raw_adj:
df.at[i, 'Reset_Flag'] = True
df.at[i, 'irrigation_depth'] = round(current_cum, 3)
df.at[i, 'isNeedirrigate'] = True
df.at[i, 'Di'] = 0 # 保留当天ETc值
df.at[i, 'irrigation_acre'] = round((current_cum*667/1000),3)
else:
df.at[i, 'Di'] = round(current_cum, 3) # 正常累加
return df
def cal_raw(etc, monitor_date,zwlx_growth_stages):
theta_fc, theta_wp = cal_soil_fc_wp()
stage, details,hight,k_rain= cal_stagebyDate(monitor_date,zwlx_growth_stages)
rd = calculate_average_root_length(details)
mad = estimate_mad_from_et(etc)
raw = mad * (theta_fc - theta_wp) * rd * 10
return raw, rd,mad,k_rain
def cal_raw_adjust(raw, f_t, f_rain, f_soil):
raw_adjust = raw * f_soil * f_t * f_rain
return raw_adjust
def cal_qxcoefficient_adjust(start_date, monitor_date, qx_data):
qx_coefficient = {}
current_date = start_date
qxData_filter = {}
for key in qx_data:
if key == "AIR_TEMPERATURE" or key == "RAIN_FALL_REALTIME":
qxData_filter[key] = qx_data[key]
qxData_filter_tj = analyze_weather_data1(qxData_filter)
now_date = datetime.now().date()
while current_date < monitor_date:
qx_futureData = []
result = {}
if (current_date + timedelta(days=3)).date() < now_date:
start = datetime.strptime((current_date + timedelta(days=1)).strftime("%Y-%m-%d"), "%Y-%m-%d")
end = datetime.strptime((current_date + timedelta(days=3)).strftime("%Y-%m-%d"), "%Y-%m-%d")
# 筛选日期范围内的数据
filter = {
date: values
for date, values in qxData_filter_tj.items()
if start <= datetime.strptime(date, "%Y-%m-%d") <= end
}
qx_futureData = transform_data(filter)
else:
try:
# print(f"进入else分支: {current_date}")
qx_futureData = getFutureWeather(current_date)
except Exception as e:
# print(f"else分支异常: {e}")
raise # 保留异常以便调试
qw_mean = (qx_futureData['qw_day1']['mean'] + qx_futureData['qw_day2']['mean'] + qx_futureData['qw_day3']['mean']) / 3
js_sum = qx_futureData['js_day1']['max'] + qx_futureData['js_day2']['max'] + qx_futureData['js_day3']['max']
qw_coff = 1.0
js_coff = 1.0
if qw_mean < 10:
qw_coff = 1.1
elif 10 < qw_mean <= 25:
qw_coff = 1
elif 25 < qw_mean <= 30:
qw_coff = 0.85
else:
qw_coff = 0.75
if js_sum <= 5:
js_coff = 1.0 # 少雨
elif 5 < js_sum <= 10:
js_coff = 1.2 # 中雨
elif 10 < js_sum <= 20:
js_coff = 1.4 # 大雨
else:
js_coff = 1.6 # 暴雨
result['qw'] = qw_coff
result['js'] = js_coff
qx_coefficient[current_date.strftime("%Y-%m-%d")] = result
current_date = current_date + timedelta(days=1)
return qx_coefficient
def transform_data(original_data):
transformed = {}
# 处理温度数据 (qw_day)
for i, (date, metrics) in enumerate(original_data.items(), 1):
temp_data = metrics["AIR_TEMPERATURE"]
transformed[f"qw_day{i}"] = {
"max": round(temp_data["max"], 2),
"min": round(temp_data["min"], 2),
"mean": round(temp_data["mean"], 2),
"sum": round(temp_data["sum"], 2)
}
# 处理降雨数据 (js_day)
for i, (date, metrics) in enumerate(original_data.items(), 1):
rain_data = metrics["RAIN_FALL_REALTIME"]
transformed[f"js_day{i}"] = {
"max": round(rain_data["max"], 2),
"min": round(rain_data["min"], 2),
"mean": round(rain_data["mean"], 2),
"sum": round(rain_data["sum"], 2)
}
return transformed
def cal_soil_coff(soil_data):
soil_data_coff = {}
for sensor in soil_data:
for date in soil_data[sensor]:
soil_cof = 1.0
sd_surf = float(soil_data['SOIL_MOISTURE_SURFACE'][date])
sd_mid = float(soil_data['SOIL_MOISTURE_MIDDLE'][date])
if sd_surf < 10:
soil_cof = 0.5
if 10 <sd_surf < 15:
soil_cof = 0.75
elif 15<sd_surf < 20:
soil_cof = 1.0
else:
soil_cof = 1.2
soil_data_coff[date] = soil_cof
return soil_data_coff
#将两块地的传感器数据分隔开
def get_soil_data_bydk(soil_data):
target_key = 7
dk1_soildata = {}
dk2_soildata = {}
for key, value in soil_data.items():
parts = key.split("号传感器")
if int(parts[0]) < target_key:
dk1_soildata[key] = value
else:
dk2_soildata[key] = value
return dk1_soildata, dk2_soildata
# 整生育期监测
def cal_dk_isNeegirrigate(zwlx_name,dk_name,start_date, monitor_date,Di_1):
auth_token = loginAuth()
date_start = start_date.strftime("%Y-%m-%d") + " 00:00:00.123"
date_end_qx=monitor_date
if (monitor_date + timedelta(days=3)).date()<= datetime.now().date():
date_end_qx=(monitor_date + timedelta(days=3)).strftime("%Y-%m-%d") + " 23:59:59.123"
else:
date_end_qx=datetime.now().date().strftime("%Y-%m-%d") + " 23:59:59.123"
date_end = monitor_date.strftime("%Y-%m-%d") + " 23:59:59.123"
startTs = dateToTimestamp(date_start)
endTs = dateToTimestamp(date_end)
zwlx_growth_stages = crop_growth_stages[zwlx_name]
kc_daily = get_daily_kc(zwlx_growth_stages)
# #获取气象数据
qxdata = getWeatherAndSoilData(auth_token, weather_params['device_id'], startTs, dateToTimestamp(date_end_qx), weather_params['agg'],
weather_params['interval'], weather_params['limit'], weather_params['orderBy'],
weather_params['keys'])
# # #彭曼公式气象入参
qx_params = get_params(start_date, monitor_date, qxdata)
# #获取土壤传感器数据
data = get_soil_data_dk1(auth_token, startTs, endTs)
soil_data = get_soil_data_night_eyveryDay(data)
dk1_soildata, dk2_soildata = get_soil_data_bydk(soil_data)
# 膜边探头优先原则1号和7号传感器分别为1号地和5号地的膜边探头
dk1_soil_mean = dk1_soildata['1号传感器']
dk2_soil_mean = dk2_soildata['7号传感器']
# #计算参考蒸散量
et0_list = cal_et0_List(start_date, monitor_date, qx_params)
#计算土壤蒸发系数
ke_data = cal_ke_daily_list(start_date, monitor_date, et0_list, kc_daily, qxdata)
ke_daily = []
rain_daily=[]
irrigate_daily=[]
for date in sorted(ke_data.keys()):
ke_value = round(ke_data[date]['ke'],3)
rain_value= round(ke_data[date]['rain'],3)
irrigate_value = ke_data[date]['irrigation']
ke_daily.append(ke_value)
rain_daily.append(rain_value)
irrigate_daily.append(irrigate_value)
# #获取作物蒸散量
etc_list, date_strings, kc_list = calculate_etc_List_new(et0_list, start_date, monitor_date, kc_daily,ke_daily)
# # 获取未来3天气象数据
qx_coff = cal_qxcoefficient_adjust(start_date, monitor_date, qxdata)
if "1" in dk_name:
df = export_etc_et0_date(et0_list, etc_list, date_strings, kc_list,ke_daily, dk1_soil_mean,rain_daily,irrigate_daily,dk_name)
# 基于每天实测土壤相对湿度计算土壤系数
soil_coff = cal_soil_coff(dk1_soil_mean)
elif "5" in dk_name:
df = export_etc_et0_date(et0_list, etc_list, date_strings, kc_list,ke_daily, dk2_soil_mean,rain_daily,irrigate_daily,dk_name)
# 基于每天实测土壤相对湿度计算土壤系数
soil_coff = cal_soil_coff(dk2_soil_mean)
else:
return "该地块数据不存在"
# 判断是否需要灌溉
dataframe = isNeedirrigate(df, qx_coff, soil_coff,Di_1,zwlx_growth_stages)
random_uuid = uuid.uuid4()
uuid_str = str(random_uuid)
# df.to_csv(
# 'D:/悦柠/遥感/甘草/ceshi/' + dk_name + "_" + start_date.strftime("%Y-%m-%d") + "-" + monitor_date.strftime(
# "%Y-%m-%d")+"_"+uuid_str + ".csv", index=False) # CSV 文件
return dataframe
def cal_dk_isNeegirrigate_Day(zwlx_name,dk_name,soil_key,start_date, monitor_date,Di_1,weather_key):
auth_token = loginAuth()
date_start = start_date.strftime("%Y-%m-%d") + " 00:00:00.123"
date_end_qx=monitor_date
if (monitor_date + timedelta(days=3)).date()<= datetime.now().date():
date_end_qx=(monitor_date + timedelta(days=3)).strftime("%Y-%m-%d") + " 23:59:59.123"
else:
date_end_qx=datetime.now().date().strftime("%Y-%m-%d") + " 23:59:59.123"
date_end = monitor_date.strftime("%Y-%m-%d") + " 23:59:59.123"
startTs = dateToTimestamp(date_start)
endTs = dateToTimestamp(date_end)
zwlx_growth_stages=crop_growth_stages[zwlx_name]
kc_daily = get_daily_kc(zwlx_growth_stages)
# #获取气象数据
qxdata = getWeatherAndSoilData(auth_token, weather_key, startTs, dateToTimestamp(date_end_qx), weather_params['agg'],
weather_params['interval'], weather_params['limit'], weather_params['orderBy'],
weather_params['keys'])
# 获取土壤传感器数据
data = get_soil_data_dk2(auth_token, soil_key, startTs, endTs)
soil_data = get_soil_data_night_eyveryDay(data)
# dk1_soildata, dk2_soildata = get_soil_data_bydk(soil_data)
# 膜边探头优先原则1号和7号传感器分别为1号地和5号地的膜边探头
dk_soil_mean = soil_data[soil_key]
if len(qxdata) !=0 and len(dk_soil_mean) !=0:
# # #彭曼公式气象入参
qx_params = get_params(start_date, monitor_date, qxdata)
# #计算参考蒸散量
et0_list = cal_et0_List(start_date, monitor_date, qx_params)
# 计算土壤蒸发系数
ke_data = cal_ke_daily_list(start_date, monitor_date, et0_list, kc_daily, qxdata, zwlx_growth_stages)
ke_daily = []
rain_daily = []
irrigate_daily = []
for date in sorted(ke_data.keys()):
ke_value = round(ke_data[date]['ke'], 3)
rain_value = round(ke_data[date]['rain'], 3)
irrigate_value = ke_data[date]['irrigation']
ke_daily.append(ke_value)
rain_daily.append(rain_value)
irrigate_daily.append(irrigate_value)
# #获取作物蒸散量
etc_list, date_strings, kc_list = calculate_etc_List_new(et0_list, start_date, monitor_date, kc_daily,
ke_daily)
# # 获取未来3天气象数据
qx_coff = cal_qxcoefficient_adjust(start_date, monitor_date, qxdata)
df = export_etc_et0_date(et0_list, etc_list, date_strings, kc_list, ke_daily, dk_soil_mean, rain_daily,
irrigate_daily, dk_name)
# 基于每天实测土壤相对湿度计算土壤系数
soil_coff = cal_soil_coff(dk_soil_mean)
# 判断是否需要灌溉
dataframe = isNeedirrigate(df, qx_coff, soil_coff, Di_1, zwlx_growth_stages)
return dataframe
elif len(qxdata) ==0 and len(dk_soil_mean) !=0:
raise ValueError("未查询到气象数据")
elif len(qxdata) !=0 and len(dk_soil_mean) ==0:
raise ValueError("未查询到土壤数据")
else:
raise ValueError("未查询到气象和土壤数据")
#计算土壤蒸发系数
def cal_ke_daily_list(start_date,monitor_date,et0_list,kc_list,qx_data,zwlx_growth_stages):
stage, details, hight ,k_rain= cal_stagebyDate(monitor_date,zwlx_growth_stages)
REW=9
I=0.0
theta_fc=0.2884
theta_wp=0.1086
current_date=start_date
qxData_tj = analyze_weather_data1(qx_data)
ke_data= {}
i=0
while current_date< monitor_date:
date_key = str(current_date.date())
u2=qxData_tj[date_key]['WIND_SPEED']['mean']
rain=qxData_tj[date_key]['RAIN_FALL_REALTIME']['max']
rh_min=qxData_tj[date_key]['AIR_HUMIDITY']['min']
if current_date == start_date:
DE_i_1 = 0
fw=0.35
else:
# 从上一天的数据中获取DE_i如果存在
prev_date = current_date - timedelta(days=1)
prev_date_key = str(prev_date.date())
if prev_date_key in ke_data:
DE_i_1 = ke_data[prev_date_key]['DE_i']
else:
DE_i_1 = 0 # 处理特殊情况如start_date等于monitor_date
if I > 0 and rain < 4:
fw = 0.35
elif I > 0 and rain < 4:
fw = 1
elif rain > 4 and I == 0:
fw = 1
else:
fw = ke_data[prev_date_key]['fw']
et0 = 0.0
kc = 0.0
if i < len(et0_list):
et0 = et0_list[i]
kc = kc_list[i]
ke,few=calculate_ke(hight, u2, rh_min, kc, DE_i_1, REW, fw, theta_fc,theta_wp)
et_soil=ke*et0
DE_i = calculate_D_ei(DE_i_1, rain, I,et_soil, fw,few)
#创建当天的数据字典
daily_data = {
'ke': ke,
'DE_i': DE_i,
'et0': et0,
'et_soil': et_soil,
'fw':fw,
"rain":rain,
"irrigation":I
}
ke_data[date_key]=daily_data
current_date = current_date + timedelta(days=1)
i=i+1
return ke_data
# 单天监测
def cal_irrigationByDay(zwlx_name,dkname,start_date,end_date,soil_key,weather_key,irrigation_really):
yesterday_date=(start_date - timedelta(days=1)).strftime("%Y-%m-%d")
condition = {"Date": yesterday_date,"dkbm":dkname}
df=query_postgresql_to_dataframe(condition)
Di_1 = 0
if irrigation_really>0:
Di_1 = 0
update_irrigation_data(yesterday_date,dkname,"irrigation_really",irrigation_really)
else:
# 将Date列转换为日期时间类型
df['Date'] = pd.to_datetime(df['Date'])
if len(df[df.Date == (start_date - timedelta(days=1))])!=0:
isNeedjg=df[df.Date == (start_date- timedelta(days=1))]['isNeedirrigate'].values[0]
if isNeedjg:
Di_1 = df[df.Date == (start_date - timedelta(days=1))]['Di-1'].values[0]+df[df.Date == (start_date - timedelta(days=1))]['ETc'].values[0]
else:
Di_1 = df[df.Date == (start_date - timedelta(days=1))]['Di'].values[0]
dataframe=cal_dk_isNeegirrigate_Day(zwlx_name,dkname,soil_key,start_date,end_date,Di_1,weather_key)
condition_new = {"Date": start_date.strftime("%Y-%m-%d"), "dkbm": dkname}
df_query=query_postgresql_to_dataframe(condition_new)
if len(df_query)==0:
dataframe_to_postgresql_batch(dataframe)
else:
print("库表中已存在该数据")
return dataframe
if __name__ == "__main__":
#调用气象数据接口--获取具体日期的气象数据(气象)
# auth_token = loginAuth()
start_date = datetime(2025, 7, 16)
monitor_date = datetime(2025, 7, 17)
dk_name = "5"
zwlx_name="甘草"
soil_key = "8637b970-561b-11f0-a556-4f10f26fc07f"
weather_key="18d121f0-561b-11f0-a556-4f10f26fc07f"
irrigation_really=5
cal_irrigationByDay(zwlx_name,dk_name,start_date,monitor_date,soil_key,weather_key,irrigation_really)
# cal_dk_isNeegirrigate(dk_name, planting_date, monitor_date,0)

371
irrgiation/mathuntils.py Normal file
View File

@@ -0,0 +1,371 @@
#根据日期计算当年天数
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
def is_leap_year(year):
return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)
def day_of_year(date_str):
date_part = date_str.split()[0]
year = int(date_str[:4]) # 取前4位 → 2025
month = int(date_str[5:7]) # 取第5-6位 → 06 → 6
day = int(date_str[8:10]) # 取第8-9位 → 30
month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if is_leap_year(year):
month_days[1] = 29 # 闰年2月29天
total = sum(month_days[:month-1]) + day
return total
#计算每天有效日照时长
def count_above_threshold(data, threshold,date):
filtered = filter(lambda x: float(x['value_cal']) >= threshold and x['datetime'].split(" ")[0] == date, data)
return len(list(filtered))
# 示例计算函数Lux转W/m²
def lux_to_wm2(lux_value):
"""将Lux值转换为W/m²使用标准系数0.0081"""
return lux_value * 0.0081
def convert_lux_to_Radiant_Flux(data_dict):
"""
递归地将字典中的所有'timestamp'字段转换为可读的日期时间格式
保留原始时间戳的同时添加新的'datetime'字段
"""
if 'value' in data_dict:
radiant_Flux=data_dict["value"]*0.001496
data_dict["value_radiant"] =radiant_Flux
return data_dict
def lux_to_solar_rad(lux,h):
return lux * 0.001496*h * 3.6 / 1000
def plot(soil_data,name):
import matplotlib.dates as mdates
# 创建画布
plt.figure(figsize=(14, 8))
# 处理并绘制地表湿度数据
surface_df = pd.DataFrame(soil_data["SOIL_MOISTURE_SURFACE"])
surface_df['datetime'] = pd.to_datetime(surface_df['datetime'])
surface_df['value'] = surface_df['value'].astype(float)
plt.plot(surface_df['datetime'], surface_df['value'],
label='Surface Moisture (0-5cm)', color='#FF6B6B', linewidth=2, marker='o', markersize=5)
# 处理并绘制中层湿度数据
middle_df = pd.DataFrame(soil_data["SOIL_MOISTURE_MIDDLE"])
middle_df['datetime'] = pd.to_datetime(middle_df['datetime'])
middle_df['value'] = middle_df['value'].astype(float)
plt.plot(middle_df['datetime'], middle_df['value'],
label='Middle Layer Moisture', color='#4ECDC4', linewidth=2, marker='s', markersize=5)
# 处理并绘制中层湿度数据
middle_df = pd.DataFrame(soil_data["SOIL_MOISTURE_BOTTOM"])
middle_df['datetime'] = pd.to_datetime(middle_df['datetime'])
middle_df['value'] = middle_df['value'].astype(float)
plt.plot(middle_df['datetime'], middle_df['value'],
label='BOTTOM Layer Moisture', color='blue', linewidth=2, marker='s', markersize=5)
# 设置图表格式
plt.title('Soil Moisture Variation (Jun 18 - Jul 3, 2025)', fontsize=16, pad=20)
plt.xlabel('Date', fontsize=12)
plt.ylabel('Soil Moisture (%)', fontsize=12)
plt.grid(True, linestyle='--', alpha=0.7)
# 设置x轴日期格式
ax = plt.gca()
ax.xaxis.set_major_locator(mdates.DayLocator(interval=2))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%m/%d'))
plt.xticks(rotation=45)
# 添加图例和注释
plt.legend(loc='upper right', fontsize=10)
plt.tight_layout()
# 保存图片到本地
output_path = 'D:\悦柠\遥感\甘草\ceshi/'+name+".png"
plt.savefig(output_path, dpi=300, bbox_inches='tight')
# 显示图表
# plt.show()
def rh_to_theta_vg(rh, soil_params, temperature=25):
"""使用Van Genuchten模型将相对湿度转换为体积含水量"""
# 计算土壤水势(kPa)
psi = -145 * np.log(rh / 100)
# 提取土壤参数
theta_s = soil_params["theta_s"] # 饱和含水量
theta_r = soil_params["theta_r"] # 残余含水量
alpha = soil_params["alpha"] # Van Genuchten参数
n = soil_params["n"] # Van Genuchten参数
m = 1 - 1 / n
# 计算体积含水量
theta = theta_r + (theta_s - theta_r) / ((1 + (alpha * abs(psi)) ** n) ** m)
print(theta)
return theta
def calculate_soil_moisture_after_irrigation(
initial_moisture: float,
field_capacity: float,
irrigation_volume: float,
soil_volume: float,
area: float = None,
verbose: bool = False
) -> float:
"""
不考虑降雨量和蒸散量时,计算灌溉后的土壤相对湿度
参数:
initial_moisture: 初始土壤相对湿度 (%)
field_capacity: 田间持水量 (%)
irrigation_volume: 灌溉量 (m³)
soil_volume: 土壤体积 (m³)
area: 灌溉面积 (㎡,可选,仅用于打印提示)
verbose: 是否打印详细计算过程
返回:
float: 灌溉后土壤相对湿度 (%)
"""
# 参数验证
if not (0 <= initial_moisture <= 100):
raise ValueError("初始湿度必须在0-100%之间")
if not (0 < field_capacity <= 100):
raise ValueError("田间持水量必须在0-100%之间且大于0")
if irrigation_volume < 0:
raise ValueError("灌溉量不能为负数")
if soil_volume <= 0:
raise ValueError("土壤体积必须大于0")
# 计算初始土壤含水量
initial_water = (initial_moisture / 100) * (field_capacity / 100) * soil_volume
# 灌溉后总含水量
total_water = initial_water + irrigation_volume
# 计算田间持水总量
max_water = (field_capacity / 100) * soil_volume
# 计算灌溉后湿度超过田间持水量时取100%
final_moisture = min((total_water / max_water) * 100, 100.0)
# if verbose:
# area_info = f"(面积: {area}㎡)" if area else ""
# print(f"===== 不考虑降雨与蒸散的灌溉计算 {area_info} =====")
# print(f"初始湿度: {initial_moisture}%")
# print(f"田间持水量: {field_capacity}%")
# print(f"土壤体积: {soil_volume}m³")
# print(f"灌溉量: {irrigation_volume}m³")
# print(f"初始含水量: {initial_water:.2f}m³")
# print(f"灌溉后含水量: {total_water:.2f}m³")
# print(f"田间最大持水量: {max_water:.2f}m³")
# print(f"灌溉后土壤湿度: {final_moisture:.2f}%")
return final_moisture
def calculate_irrigation_volume(
initial_moisture: float,
target_moisture: float,
field_capacity: float,
soil_volume: float
) -> float:
"""
计算为达到目标土壤相对湿度所需的灌溉量
参数:
initial_moisture: 初始土壤相对湿度 (%)
target_moisture: 目标土壤相对湿度 (%)
field_capacity: 田间持水量 (%)
soil_volume: 土壤体积 (m³)
area: 灌溉面积 (㎡,可选,仅用于打印提示)
verbose: 是否打印详细计算过程
返回:
float: 需要的灌溉量 (m³)
"""
# 参数验证
if not (0 <= initial_moisture <= 100):
raise ValueError("初始湿度必须在0-100%之间")
if not (0 <= target_moisture <= 100):
raise ValueError("目标湿度必须在0-100%之间")
if not (0 < field_capacity <= 100):
raise ValueError("田间持水量必须在0-100%之间且大于0")
if soil_volume <= 0:
raise ValueError("土壤体积必须大于0")
if target_moisture <= initial_moisture:
raise ValueError("目标湿度必须大于初始湿度")
# 计算初始土壤含水量
initial_water = (initial_moisture / 100) * (field_capacity / 100) * soil_volume
# 计算目标含水量
target_water = (target_moisture / 100) * (field_capacity / 100) * soil_volume
# 需要的灌溉量
irrigation_volume = target_water - initial_water
return irrigation_volume
def irriDepth_irrvolume(area,irrDepth):
volume=area*667*(irrDepth/1000)
return volume
def calculate_irrigation(dry_soil_weight, current_humidity, target_humidity,
irrigation_efficiency=0.8, water_density=1000):
"""
计算达到目标湿度所需的灌溉量
Args:
dry_soil_weight (float): 土壤干重kg
current_humidity (float): 当前相对湿度(%
target_humidity (float): 目标相对湿度(%
irrigation_efficiency (float): 灌溉效率0~1
water_density (float): 水密度kg/m³
Returns:
float: 灌溉量(立方米)
"""
if current_humidity >= target_humidity:
return 0.0
delta_humidity = (target_humidity - current_humidity) / 100
water_weight = dry_soil_weight * delta_humidity
water_volume = water_weight / water_density
return water_volume / irrigation_efficiency
def calculate_rh_target(
initial_moisture: float,
root: float,
field_capacity: float,
irrigation:float
) -> float:
initial_moisture=(initial_moisture / 100) * field_capacity
iir_volume=irrigation/(10*root)
target_moisture = initial_moisture + (iir_volume/field_capacity)
return target_moisture
def calculate_rh_target(
initial_moisture: float, # 初始土壤相对湿度(%)
root: float, # 根系深度(cm)
field_capacity: float, # 田间持水量(体积含水量 cm³/cm³)
irrigation: float # 灌溉量(mm)
) -> float:
"""
计算灌溉后的目标土壤相对湿度(%)
参数:
initial_moisture: 初始土壤相对湿度(%)
root: 根系深度(cm)
field_capacity: 田间持水量(体积含水量 cm³/cm³)
irrigation: 灌溉量(mm)
返回:
float: 灌溉后的目标土壤相对湿度(%)
"""
# 参数验证
if initial_moisture < 0 or initial_moisture > 100:
raise ValueError("初始湿度必须在0-100%之间")
if root <= 0:
raise ValueError("根系深度必须大于0")
if field_capacity <= 0 or field_capacity > 1:
raise ValueError("田间持水量必须在0-1 cm³/cm³之间")
if irrigation < 0:
raise ValueError("灌溉量不能为负值")
# 将相对湿度转为体积含水量
initial_volume_moisture = (initial_moisture / 100) * field_capacity
# 计算灌溉增加的体积含水量
# 将mm转为cm并除以根系深度得到体积含水量增量
moisture_increase = (irrigation / 10) / root
# 计算灌溉后的体积含水量
target_volume_moisture = initial_volume_moisture + moisture_increase
# 转换回相对湿度(%)
target_rh = (target_volume_moisture / field_capacity) * 100
# 确保不超过100%
target_rh = min(target_rh, 100.0)
# # 3. 计算目标体积含水量 (不超过田间持水量)
# target_volume = min(initial_volume + moisture_increase, field_capacity)
#
# # 4. 转换回相对湿度 (%)
# target_rh = (target_volume / field_capacity) * 100
return target_rh
def calculate_rh_target_rz(
initial_moisture: float, # 初始土壤相对湿度(%)
soil_root: float, # 根系深度(cm)
area: float, # 灌溉面积(亩)
irrigation: float, # 灌溉量(mm)
field_capacity: float = 28.84, # 田间持水量(%)默认35%
soil_bulk_density: float = 1.5 # 土壤容重(g/cm³)默认1.5
) -> float:
"""
计算灌溉后的目标根区土壤相对湿度
参数:
initial_moisture: 初始土壤相对湿度(%)
soil_root: 根系深度(cm)
area: 灌溉面积(亩)
irrigation: 灌溉量(mm)
field_capacity: 田间持水量(%)默认35%
soil_bulk_density: 土壤容重(g/cm³)默认1.5
返回:
float: 灌溉后的目标土壤相对湿度(%)
"""
# 参数验证
if initial_moisture < 0 or initial_moisture > 100:
raise ValueError("初始湿度必须在0-100%之间")
if soil_root <= 0:
raise ValueError("根系深度必须大于0")
if area <= 0:
raise ValueError("面积必须大于0")
if irrigation < 0:
raise ValueError("灌溉量不能为负值")
# 计算根区体积
root_volume = area * 667 * (soil_root / 100) # m³将cm转为m
# 计算灌溉水体积
water_volume = (irrigation / 1000) * area * 667 # m³将mm转为m
# 计算灌溉增加的土壤湿度(%)
moisture_increase = (water_volume / (root_volume * soil_bulk_density)) * 100
# 计算目标湿度
target_moisture = initial_moisture + moisture_increase
# 确保不超过田间持水量
target_moisture = min(target_moisture, field_capacity)
return target_moisture
if __name__ == '__main__':
# volume=irriDepth_irrvolume(10.95,11.123)
# soil_volume_dk1 = 10.95 * 667 * 0.15
# soil_volume_dk2 = 8.92 * 667 * 0.15
# result1 = calculate_soil_moisture_after_irrigation(
# initial_moisture=15.2,
# field_capacity=28.84,
# irrigation_volume=168,
# soil_volume=soil_volume_dk1,
# verbose=True
# )
# print(f"\n示例1结果: 灌溉后湿度 = {result1:.2f}%\n")
# print(soil_volume_dk1)
# result2=calculate_irrigation_volume(initial_moisture=15.44,
# field_capacity=28.84,
# target_moisture=70,
# soil_volume=soil_volume_dk1)
# print(f"\n达到目标湿度所需灌溉量 = {result2:.2f}\n")
# # 调用示例
# dry_soil_weight=10.95*667*0.1*1200
# volume = calculate_irrigation(
# dry_soil_weight=dry_soil_weight,
# current_humidity=15.4,
# target_humidity=70
# )
# print(f"灌溉量: {volume:.2f} m³") # 输出: 0.09 m³
rh=calculate_rh_target(67.96,6,0.2884,33.14)
rh1 = rh*0.7
print(rh,rh1)

133
irrgiation/soil_Ke.py Normal file
View File

@@ -0,0 +1,133 @@
# 计算土壤蒸发系数
def calculate_ke(h, u2, rh_min, k_cb, DE_i_1,REW,fw,theta_fc,theta_wp):
"""
计算土壤蒸发系数 ke
参数:
h: 株高(m)
u2: 2米高处风速(m/s)
rh_min: 最小相对湿度(%)
k_cb: 基础作物系数
DE_i_1: 前一天从土壤表层蒸发的累积深度(mm)
TEW: 总可蒸发水量(mm)
REW: 易蒸发水量(mm)
few: 裸露湿润的土壤的比值
返回:
ke: 土壤蒸发系数
"""
K_cmin = 0.175
Z_e=0.12
kc_max = calculate_kc_max(h, u2, rh_min, k_cb)
fc = calculate_fc(k_cb, K_cmin, kc_max, h)
few = calculate_fev(fc, fw)
TEW = calculate_tew(theta_fc, theta_wp, Z_e)
kr = calculate_Kr(DE_i_1, TEW, REW)
term1 = kr * (kc_max - k_cb)
term2 = few * k_cb
return min(term1, term2),few
# 每日土壤蒸发量累积量
#土壤蒸发有效部分
def calculate_fev(fc, fw):
fev = min(1 - fc, fw)
return fev
#计算作物覆盖率fc
def calculate_fc(Kcb, Kcmin, Kcmax, h):
#Kcb作物基础系数Kcmin无地表覆盖的干燥土壤最小 值(约为 0.150.20Kcmax紧随湿润过程的土壤最大 值
fc = ((Kcb- Kcmin) / (Kcmax - Kcmin)) ** (1 + 0.5 * h)
return fc
# 计算蒸发减少系数
def calculate_Kr(DE_i_1, TEW, REW):
if DE_i_1 <= REW:
Kr = 1
else:
print("dd")
Kr = (TEW - DE_i_1) / (TEW - REW)
return Kr
#计算Kcmax
def calculate_kc_max(h: float, u2: float, rh_min: float, k_cb: float) -> float:
"""
计算作物系数上限 kc_max
参数:
h: 株高(m)
u2: 2米高处风速(m/s)
rh_min: 最小相对湿度(%)
k_cb: 基础作物系数
返回:
kc_max: 作物系数上限
"""
term1 = 1.2 + (0.04 * (u2 - 2) - 0.004 * (rh_min - 45)) * (h / 3) ** 0.3
term2 = k_cb + 0.5
return max(term1, term2)
#计算TEW
def calculate_tew(theta_fc: float, theta_wp: float, Z_e: float) -> float:
"""
计算总可蒸发水量 TEW
参数:
theta_fc: 田间持水量(m³/m³)
theta_wp: 萎蔫系数(m³/m³)
Z_e: 土壤蒸发层深度(cm)
返回:
TEW: 总可蒸发水量(mm)
"""
return 1000 * (theta_fc - theta_wp) * Z_e
# 计算深层渗漏损失
def calculate_DP_ei(P_i, RO_i, I_i, D_e_prev, f_w):
if (P_i - RO_i) + I_i / f_w - D_e_prev >= 0:
return (P_i - RO_i) + I_i / f_w - D_e_prev
else:
return 0
# 计算每日土壤蒸发量累积量
def calculate_D_ei(D_e_prev, P_i, I_i, E_i, f_w, f_ew):
RO_i=0
T_ew_i=0
DP_ei = calculate_DP_ei(P_i, RO_i, I_i, D_e_prev, f_w)
D_ei = D_e_prev - (P_i - RO_i) - I_i / f_w + E_i / f_ew + T_ew_i + DP_ei
return D_ei
# 示例计算
if __name__ == "__main__":
# # 示例参数
# h = 0.07 # 株高(m)
# u2 = 3.0 # 2米高处风速(m/s)
# rh_min = 20.0 # 最小相对湿度(%)
# k_cb = 0.4 # 基础作物系数
# K_s = 0.6 # 土壤导水率
# K_cmin = 0.175
#
# fw = 0.35 # 土壤表层被灌溉浸湿的部分
# DE_i = 6.24 # 前一天从土壤表层蒸发的累积深度(mm)
# DE_i_1 = 2.965 # 前一天从土壤表层蒸发的累积深度(mm)
# theta_fc = 0.2884 # 田间持水量(m³/m³)
# theta_wp = 0.1086 # 萎蔫系数(m³/m³)
# Z_e = 0.1 # 土壤蒸发层深度(m)
#
# # # # 计算 TEW
# # TEW = calculate_tew(theta_fc, theta_wp, Z_e)
# REW = 5.178 # 假设REW为TEW的40%
# # #
# # # # 计算各参数
# # kc_max = calculate_kc_max(h, u2, rh_min, k_cb)
# # fc = calculate_fc(k_cb, K_cmin, kc_max, h)
# # few= calculate_fev(fc, fw)
# # kr = calculate_Kr(DE_i_1,DE_i, TEW, REW)
# ke=calculate_ke(h, u2, rh_min, k_cb, DE_i_1,DE_i,REW)
# # 输出结果
#
# print(f"土壤蒸发系数 Ke: {ke:.4f}")
# 假设初始参数
TEW = 1000 # 总可蒸发水量单位mm
D_e_start = TEW # 模拟开始时的初始D_e
D_e_prev = 0 # 模拟第一天的D_e假设充分灌溉后为0
# 假设一些每日数据,实际应用中需替换为真实数据
P_i = 10 # 第i天的降雨量单位mm
RO_i = 0 # 第i天的降雨形成地表径流量这里假定为0单位mm
I_i = 19 # 第i天渗入土壤的灌溉量单位mm
k_e = 0.14 # 土壤蒸发系数
ET_o = 8.188 # 参考作物蒸散量单位mm
E_i = k_e * ET_o # 第i天的蒸发量
T_ew_i = 0 # 第i天湿润裸露表层土壤的蒸腾量这里假定为0单位mm
f_w = 0.8 # 经灌溉湿润的土壤面积比值
f_ew = 0.6 # 裸露湿润土壤面积比值
D_ei = calculate_D_ei(D_e_prev, P_i, RO_i, I_i, f_w, f_ew)
print(f"第i天末土壤完全湿润后的累积蒸发消耗量为: {D_ei} mm")

10
irrgiation/utils.py Normal file
View File

@@ -0,0 +1,10 @@
dk_irriationInfo={
"1号地":{
"dkbm":1,
"irriationFile":"D:/悦柠/遥感/甘草/model_result/1号地_灌溉数据.xlsx"
},
"5号地":{
"dkbm":5,
"irriationFile":"D:/悦柠/遥感/甘草/model_result/5号地_灌溉数据.xlsx"
}
}

View File

@@ -0,0 +1,336 @@
from datetime import datetime, timezone, timedelta, date
import pandas as pd
import requests
import json
from matplotlib import pyplot as plt
#登录接口认证
def loginAuth():
# 接口地址
global auth_token
url = "https://xj-tb.maimaiag.com/api/auth/login" # 请替换为实际地址
# 请求头
headers = {
"Content-Type": "application/json"
}
# 请求体数据
payload = {
"username": "read@xj.com",
"password": "maimai"
}
try:
# 发送POST请求
response = requests.post(
url,
headers=headers,
data=json.dumps(payload) # 或者直接使用 json=payload
)
# 检查响应状态码
if response.status_code == 200:
print("登录成功!")
print("响应数据:", response.json())
# 通常登录接口会返回token可以这样获取
response_data = response.json()
auth_token = response_data.get("token") # 根据实际返回字段调整
if auth_token:
print("获取到的认证Token:", auth_token)
else:
print(f"登录失败,状态码: {response.status_code}")
print("错误信息:", response.text)
except requests.exceptions.RequestException as e:
print(f"请求发生异常: {str(e)}")
except json.JSONDecodeError as e:
print(f"JSON解析错误: {str(e)}")
return auth_token
# 调用数据接口--历史数据
def getWeatherAndSoilData(auth_token,device_id,startTs,endTs,agg,interval,limit,orderBy,keys):
# 配置信息
BASE_URL = "https://xj-tb.maimaiag.com/" # 请替换为实际API地址
TOKEN = auth_token # 替换为实际的Bearer token
DEVICE_ID = device_id # 替换为实际的设备ID
# 构造请求URL
endpoint = f"/api/plugins/telemetry/DEVICE/{DEVICE_ID}/values/timeseries"
url = BASE_URL + endpoint
# 请求头
headers = {
"Content-Type": "application/json",
"X-Authorization": f"Bearer+{TOKEN}"
}
# 查询参数
params = {
"keys": keys, # 要查询的参数,多个用逗号分隔
"startTs": startTs, # 开始时间戳(毫秒)- 可选
"endTs": endTs, # 结束时间戳(毫秒)- 可选
"agg": agg, # 聚合函数 - 可选
"interval": interval, # 聚合间隔(毫秒)- 可选
"limit": limit, # 返回条数 - 可选
"orderBy": orderBy # 排序方式 - 可选
}
try:
# 发送GET请求
response = requests.get(
url,
headers=headers,
params=params
)
# 检查响应状态码
if response.status_code == 200:
# print("请求成功!")
data = response.json()
# print("响应数据:", json.dumps(data, indent=2))
converted_data = {k: convert_timestamp_to_datetime(v) for k, v in data.items()}
return converted_data
elif response.status_code == 401:
print("认证失败请检查Token是否正确")
elif response.status_code == 404:
print("设备不存在或路径错误")
else:
print(f"请求失败,状态码: {response.status_code}")
print("错误信息:", response.text)
except requests.exceptions.RequestException as e:
print(f"请求发生异常: {str(e)}")
except json.JSONDecodeError as e:
print(f"JSON解析错误: {str(e)}")
print("原始响应:", response.text)
def convert_timestamp_to_datetime(data_dict):
"""
递归地将字典中的所有'timestamp'字段转换为可读的日期时间格式
保留原始时间戳的同时添加新的'datetime'字段
"""
if isinstance(data_dict, dict):
if "ts" in data_dict:
# 转换毫秒时间戳为datetime对象
timestamp_sec = data_dict["ts"] / 1000
dt = datetime.fromtimestamp(timestamp_sec)
# 添加新字段,保留原始时间戳
data_dict["datetime"] = dt.strftime("%Y-%m-%d %H:%M:%S")
return data_dict
elif isinstance(data_dict, list):
return [convert_timestamp_to_datetime(item) for item in data_dict]
else:
return data_dict
def dateToTimestamp(date):
# dt = datetime.strptime(date, "%Y-%m-%d %H:%M:%S")
if isinstance(date, datetime):
# 如果已经是datetime对象直接使用
dt = date
elif isinstance(date, str):
# 如果是字符串解析为datetime对象
dt = datetime.strptime(date, "%Y-%m-%d %H:%M:%S.%f")
else:
raise TypeError("date参数必须是字符串或datetime对象")
timestamp_ms = int(dt.timestamp() * 1000)
return timestamp_ms
#调用接口获取未来气象数据
def getFutureWeather(monitor_date):
base_url_future="https://api.open-meteo.com/"
end_point="v1/forecast"
current_date = datetime.now().date()
params = {
"latitude": "41.34252110189814", # 纬度
"longitude": "86.28810755462379", # 经度
"hourly": "temperature_2m,precipitation,precipitation_probability"
}
# if monitor_date.date()<current_date:
# # url = base_url_history + end_point
# params["start_date"] = (monitor_date + timedelta(days=1)).strftime("%Y-%m-%d")
# params["end_date"] = (monitor_date + timedelta(days=3)).strftime("%Y-%m-%d")
#
# else:
url = base_url_future + end_point
params["forecast_days"]= 4
try:
# 发送GET请求
response = requests.get(
url,
params=params
)
# 检查响应状态码
if response.status_code == 200:
data = response.json()
result={}
if monitor_date.date()<current_date:
result["qw_day1"] = get_tjvalue(data['hourly']['temperature_2m'][:24])
result["qw_day2"] = get_tjvalue(data['hourly']['temperature_2m'][24:48])
result["qw_day3"] = get_tjvalue(data['hourly']['temperature_2m'][48:72])
result["js_day1"] = get_tjvalue(data['hourly']['precipitation'][:24])
result["js_day2"] = get_tjvalue(data['hourly']['precipitation'][24:48])
result["js_day3"] = get_tjvalue(data['hourly']['precipitation'][48:72])
else:
result["qw_day1"] = get_tjvalue(data['hourly']['temperature_2m'][24:48])
result["qw_day2"] = get_tjvalue(data['hourly']['temperature_2m'][48:72])
result["qw_day3"] = get_tjvalue(data['hourly']['temperature_2m'][72:96])
result["js_day1"] = get_tjvalue(data['hourly']['precipitation'][24:48])
result["js_day2"] = get_tjvalue(data['hourly']['precipitation'][48:72])
result["js_day3"] = get_tjvalue(data['hourly']['precipitation'][72:96])
return result
elif response.status_code == 401:
print("认证失败请检查Token是否正确")
elif response.status_code == 404:
print("设备不存在或路径错误")
else:
print(f"请求失败,状态码: {response.status_code}")
print("错误信息:", response.text)
except requests.exceptions.RequestException as e:
print(f"请求发生异常: {str(e)}")
except json.JSONDecodeError as e:
print(f"JSON解析错误: {str(e)}")
print("原始响应:", response.text)
def get_tjvalue(data):
result={}
max_val = max(data)
min_val = min(data)
mean_val = sum(data) / len(data)
sum_val = sum(data)
result['max']=max_val
result['min']=min_val
result['mean']=mean_val
result['sum'] = sum_val
return result
soil_params={
"1号传感器":{
"device_id":"31fbb2d0-561b-11f0-a556-4f10f26fc07f"
},
"2号传感器": {
"device_id": "3bf4cc90-561b-11f0-a556-4f10f26fc07f"
},
"3号传感器": {
"device_id": "43a57cf0-561b-11f0-a556-4f10f26fc07f"
},
"4号传感器": {
"device_id": "4ff6b780-561b-11f0-a556-4f10f26fc07f"
},
"5号传感器": {
"device_id": "598c5480-561b-11f0-a556-4f10f26fc07f"
},
"6号传感器": {
"device_id": "6ff07260-561b-11f0-a556-4f10f26fc07f"
},
"7号传感器": {
"device_id": "8637b970-561b-11f0-a556-4f10f26fc07f"
},
"8号传感器": {
"device_id": "955de5f0-561b-11f0-a556-4f10f26fc07f"
},
"9号传感器": {
"device_id": "a24e94d0-561b-11f0-a556-4f10f26fc07f"
},
"10号传感器": {
"device_id": "adb7e060-561b-11f0-a556-4f10f26fc07f"
},
"11号传感器": {
"device_id": "b74c6bf0-561b-11f0-a556-4f10f26fc07f"
},
"12号传感器": {
"device_id": "c0f36e10-561b-11f0-a556-4f10f26fc07f"
},
}
def get_soil_data_dk1(auth_token,startTs, endTs):
keys_soil = "SOIL_MOISTURE_SURFACE,SOIL_MOISTURE_MIDDLE,SOIL_MOISTURE_BOTTOM"
limit = ""
orderBy = ""
result={}
for key in soil_params:
id=soil_params[key]['device_id']
soil_data = getWeatherAndSoilData(auth_token,id, startTs, endTs, "AVG", 3600000, limit, orderBy,
keys_soil)
result[key]=soil_data
return result
def get_soil_data_dk2(auth_token,device_id,startTs, endTs):
keys_soil = "SOIL_MOISTURE_SURFACE,SOIL_MOISTURE_MIDDLE,SOIL_MOISTURE_BOTTOM"
limit = ""
orderBy = ""
result={}
soil_data = getWeatherAndSoilData(auth_token,device_id, startTs, endTs, "AVG", 3600000, limit, orderBy,
keys_soil)
result[device_id]=soil_data
return result
weather_params={
"device_id" : "18d121f0-561b-11f0-a556-4f10f26fc07f",
"agg" :"AVG",
"interval" : 3600000, #1小时
"limit" :"",
"orderBy" : "",
"keys":"AIR_TEMPERATURE,WIND_SPEED,AIR_LUX,AIR_HUMIDITY,RAIN_FALL_REALTIME"
}
def get_plot(data_list):
df = pd.DataFrame(data_list)
df['value'] = pd.to_numeric(df['value'], errors='coerce').round(3)# 转换湿度值为浮点数
df['datetime'] = pd.to_datetime(df['datetime']) # 转换为datetime类型
# 设置画布大小
plt.figure(figsize=(12, 6))
# 绘制折线图
plt.plot(df['datetime'], df['value'],
color='teal',
linestyle='-',
linewidth=2,
marker='o',
markersize=4,
markerfacecolor='orange',
markeredgecolor='orange',
label='Soil Moisture (%)')
# 添加22点后的数据高亮符合你之前获取夜间数据的需求
night_data = df[df['datetime'].dt.hour >= 22]
plt.scatter(night_data['datetime'], night_data['value'],
color='red',
s=30,
label='22:00-06:00 Data')
# 设置标题和坐标轴标签
plt.title('Soil Moisture Time Series (2025-06-22 to 2025-06-30)',
fontsize=14,
pad=15)
plt.xlabel('Datetime', fontsize=12, labelpad=10)
plt.ylabel('Soil Moisture (%)', fontsize=12, labelpad=10)
# 设置x轴刻度旋转避免重叠
plt.xticks(rotation=45, ha='right', fontsize=10)
# 添加网格线
plt.grid(axis='y', linestyle='--', alpha=0.7)
# 添加图例
plt.legend(loc='upper right', fontsize=10)
# 调整布局
plt.tight_layout()
# 显示图表
plt.show()
if __name__ == "__main__":
auth_token = loginAuth()
device_id="18d121f0-561b-11f0-a556-4f10f26fc07f"
agg="AVG"
interval=21600000
limit=""
orderBy=""
keys="RAIN_FALL_REALTIME"
date_start = "2025-06-25 00:00:00.123"
date_end = "2025-07-01 23:59:59.123"
startTs=dateToTimestamp(date_start)
endTs=dateToTimestamp(date_end)
# data=get_soil_data_dk1(auth_token,startTs,endTs)
# print(data)
data=getWeatherAndSoilData(auth_token,device_id,startTs,endTs,"AVG",3600000,limit,orderBy,keys)
print(data['RAIN_FALL'])
# get_plot(data['1号传感器']['SOIL_MOISTURE_SURFACE'])
get_plot(data['RAIN_FALL_REALTIME'])