306 lines
13 KiB
C++
306 lines
13 KiB
C++
#include "Wind2ERANc.h"
|
||
#include "WindDataFileOperator.h"
|
||
|
||
|
||
|
||
DataFileInfo get_data_file_info(const std::string& filepath) {
|
||
DataFileInfo info;
|
||
// 这里需要实现从二进制文件读取头信息的逻辑
|
||
std::ifstream file(filepath, std::ios::binary);
|
||
if (file) {
|
||
file.read(reinterpret_cast<char*>(&info), sizeof(DataFileInfo));
|
||
file.close();
|
||
}
|
||
return info;
|
||
}
|
||
|
||
std::vector<int64_t> get_wind_data_file_time_arr(const std::string& filepath, const DataFileInfo& info) {
|
||
std::vector<int64_t> time_arr(info.Num);
|
||
if (get_WindDataFileTimeArr(filepath.c_str(), info, time_arr.data()) == 0) {
|
||
return time_arr;
|
||
}
|
||
else {
|
||
std::vector<int64_t> time_arra(0);
|
||
return time_arra;
|
||
}
|
||
|
||
|
||
|
||
}
|
||
|
||
bool read_wind_data_file(const std::string& filepath, const DataFileInfo& info,
|
||
int64_t time_id, std::vector<double>& u_arr, std::vector<double>& v_arr) {
|
||
if (Read_WindDataFile(filepath.c_str(),
|
||
info,
|
||
time_id, u_arr.data(), v_arr.data()) == 0) {
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
|
||
// 处理NetCDF错误
|
||
void ERA5NetCDFConverter::handle_netcdf_error(int status, const std::string& operation) {
|
||
if (status != NC_NOERR) {
|
||
std::cerr << "NetCDF错误 " << operation << ": " << nc_strerror(status) << std::endl;
|
||
throw std::runtime_error("NetCDF操作失败");
|
||
}
|
||
}
|
||
|
||
// 将Unix时间戳转换为ERA5使用的时间格式
|
||
std::vector<double> ERA5NetCDFConverter::convert_to_era5_time(const std::vector<int64_t>& unix_timestamps) {
|
||
std::vector<double> era5_times;
|
||
era5_times.reserve(unix_timestamps.size());
|
||
|
||
// ERA5时间通常是从1900-01-01开始的小时数
|
||
const auto era5_epoch = std::chrono::system_clock::from_time_t(-2208988800); // 1900-01-01
|
||
|
||
for (auto timestamp : unix_timestamps) {
|
||
auto time_point = std::chrono::system_clock::from_time_t(timestamp);
|
||
auto duration = std::chrono::duration_cast<std::chrono::hours>(time_point - era5_epoch);
|
||
era5_times.push_back(static_cast<double>(duration.count()));
|
||
}
|
||
|
||
return era5_times;
|
||
}
|
||
|
||
// 创建经纬度坐标数组
|
||
std::vector<double> ERA5NetCDFConverter::create_coordinate_array(double min, double max, size_t size) {
|
||
std::vector<double> coord(size);
|
||
double step = (max - min) / (size - 1);
|
||
for (size_t i = 0; i < size; ++i) {
|
||
coord[i] = min + i * step;
|
||
}
|
||
return coord;
|
||
}
|
||
|
||
bool ERA5NetCDFConverter::convert_to_era5_netcdf(const std::string& input_bin_file, const std::string& output_nc_file) {
|
||
try {
|
||
// 1. 读取二进制文件信息
|
||
DataFileInfo info = getDataFileInfo(input_bin_file.c_str());
|
||
std::cout << "文件信息: " << info.Height << "x" << info.Width
|
||
<< " 网格, " << info.Num << " 个时间步" << std::endl;
|
||
|
||
// 2. 获取时间数组
|
||
std::vector<int64_t> time_arr = get_wind_data_file_time_arr(input_bin_file.c_str(), info);
|
||
if (time_arr.empty()) {
|
||
std::cerr << "无法读取时间数组" << std::endl;
|
||
return false;
|
||
}
|
||
|
||
// 3. 创建NetCDF文件
|
||
int ncid, status;
|
||
status = nc_create(output_nc_file.c_str(), NC_NETCDF4, &ncid);
|
||
handle_netcdf_error(status, "创建文件");
|
||
|
||
// 4. 定义维度
|
||
int lon_dim, lat_dim, time_dim;
|
||
status = nc_def_dim(ncid, "longitude", info.Width, &lon_dim);
|
||
handle_netcdf_error(status, "定义经度维度");
|
||
status = nc_def_dim(ncid, "latitude", info.Height, &lat_dim);
|
||
handle_netcdf_error(status, "定义纬度维度");
|
||
status = nc_def_dim(ncid, "time", NC_UNLIMITED, &time_dim);
|
||
handle_netcdf_error(status, "定义时间维度");
|
||
|
||
// 5. 定义坐标变量
|
||
int lon_var, lat_var, time_var;
|
||
int dims_coord[1] = { lon_dim };
|
||
status = nc_def_var(ncid, "longitude", NC_DOUBLE, 1, dims_coord, &lon_var);
|
||
handle_netcdf_error(status, "定义经度变量");
|
||
|
||
dims_coord[0] = lat_dim;
|
||
status = nc_def_var(ncid, "latitude", NC_DOUBLE, 1, dims_coord, &lat_var);
|
||
handle_netcdf_error(status, "定义纬度变量");
|
||
|
||
dims_coord[0] = time_dim;
|
||
status = nc_def_var(ncid, "time", NC_DOUBLE, 1, dims_coord, &time_var);
|
||
handle_netcdf_error(status, "定义时间变量");
|
||
|
||
// 6. 定义数据变量(U和V分量)
|
||
int u10_var, v10_var;
|
||
int dims_data[3] = { time_dim, lat_dim, lon_dim };
|
||
status = nc_def_var(ncid, "u10", NC_DOUBLE, 3, dims_data, &u10_var);
|
||
handle_netcdf_error(status, "定义u10变量");
|
||
status = nc_def_var(ncid, "v10", NC_DOUBLE, 3, dims_data, &v10_var);
|
||
handle_netcdf_error(status, "定义v10变量");
|
||
|
||
// 7. 添加属性
|
||
std::string time_units = "hours since 1900-01-01 00:00:00.0";
|
||
status = nc_put_att_text(ncid, time_var, "units", time_units.length(), time_units.c_str());
|
||
handle_netcdf_error(status, "添加时间单位属性");
|
||
|
||
status = nc_put_att_text(ncid, lon_var, "units", 12, "degrees_east");
|
||
status = nc_put_att_text(ncid, lat_var, "units", 13, "degrees_north");
|
||
status = nc_put_att_text(ncid, u10_var, "units", 6, "m s**-1");
|
||
status = nc_put_att_text(ncid, v10_var, "units", 6, "m s**-1");
|
||
status = nc_put_att_text(ncid, u10_var, "long_name", 25, "10 metre U wind component");
|
||
status = nc_put_att_text(ncid, v10_var, "long_name", 25, "10 metre V wind component");
|
||
|
||
// 8. 结束定义模式
|
||
status = nc_enddef(ncid);
|
||
handle_netcdf_error(status, "结束定义模式");
|
||
|
||
// 9. 写入坐标数据
|
||
std::vector<double> lon_data = create_coordinate_array(info.minLon, info.maxLon, info.Width);
|
||
std::vector<double> lat_data = create_coordinate_array(info.minLat, info.maxLat, info.Height);
|
||
std::reverse(lat_data.begin(), lat_data.end());
|
||
std::vector<double> era5_time = convert_to_era5_time(time_arr);
|
||
|
||
status = nc_put_var_double(ncid, lon_var, lon_data.data());
|
||
handle_netcdf_error(status, "写入经度数据");
|
||
status = nc_put_var_double(ncid, lat_var, lat_data.data());
|
||
handle_netcdf_error(status, "写入纬度数据");
|
||
status = nc_put_var_double(ncid, time_var, era5_time.data());
|
||
handle_netcdf_error(status, "写入时间数据");
|
||
|
||
// 10. 写入风场数据
|
||
std::vector<double> u_data(info.Height * info.Width);
|
||
std::vector<double> v_data(info.Height * info.Width);
|
||
|
||
size_t start[3] = { 0, 0, 0 };
|
||
size_t count[3] = { 1, info.Height, info.Width };
|
||
|
||
for (int64_t t = 0; t < info.Num; ++t) {
|
||
if (read_wind_data_file(input_bin_file, info, t, u_data, v_data)) {
|
||
start[0] = t;
|
||
status = nc_put_vara_double(ncid, u10_var, start, count, u_data.data());
|
||
handle_netcdf_error(status, "写入u10数据");
|
||
status = nc_put_vara_double(ncid, v10_var, start, count, v_data.data());
|
||
handle_netcdf_error(status, "写入v10数据");
|
||
|
||
if (t % 10 == 0) {
|
||
std::cout << "已处理 " << t + 1 << "/" << info.Num << " 个时间步" << std::endl;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 11. 关闭文件
|
||
status = nc_close(ncid);
|
||
handle_netcdf_error(status, "关闭文件");
|
||
|
||
std::cout << "转换完成!输出文件: " << output_nc_file << std::endl;
|
||
return true;
|
||
|
||
}
|
||
catch (const std::exception& e) {
|
||
std::cerr << "转换过程出错: " << e.what() << std::endl;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
bool ERA5NetCDFConverter::convert_to_era5_netcdf(const std::string& input_bin_file, const std::string& output_nc_file, int64_t tid)
|
||
{
|
||
try {
|
||
// 1. 读取二进制文件信息
|
||
DataFileInfo info = getDataFileInfo(input_bin_file.c_str());
|
||
std::cout << "文件信息: " << info.Height << "x" << info.Width
|
||
<< " 网格, " << info.Num << " 个时间步" << std::endl;
|
||
|
||
// 2. 获取时间数组
|
||
std::vector<int64_t> time_arr = get_wind_data_file_time_arr(input_bin_file.c_str(), info);
|
||
if (time_arr.empty()) {
|
||
std::cerr << "无法读取时间数组" << std::endl;
|
||
return false;
|
||
}
|
||
|
||
// 3. 创建NetCDF文件
|
||
int ncid, status;
|
||
status = nc_create(output_nc_file.c_str(), NC_NETCDF4, &ncid);
|
||
handle_netcdf_error(status, "创建文件");
|
||
|
||
// 4. 定义维度
|
||
int lon_dim, lat_dim, time_dim;
|
||
status = nc_def_dim(ncid, "longitude", info.Width, &lon_dim);
|
||
handle_netcdf_error(status, "定义经度维度");
|
||
status = nc_def_dim(ncid, "latitude", info.Height, &lat_dim);
|
||
handle_netcdf_error(status, "定义纬度维度");
|
||
status = nc_def_dim(ncid, "time", NC_UNLIMITED, &time_dim);
|
||
handle_netcdf_error(status, "定义时间维度");
|
||
|
||
// 5. 定义坐标变量
|
||
int lon_var, lat_var, time_var;
|
||
int dims_coord[1] = { lon_dim };
|
||
status = nc_def_var(ncid, "longitude", NC_DOUBLE, 1, dims_coord, &lon_var);
|
||
handle_netcdf_error(status, "定义经度变量");
|
||
|
||
dims_coord[0] = lat_dim;
|
||
status = nc_def_var(ncid, "latitude", NC_DOUBLE, 1, dims_coord, &lat_var);
|
||
handle_netcdf_error(status, "定义纬度变量");
|
||
|
||
dims_coord[0] = time_dim;
|
||
status = nc_def_var(ncid, "time", NC_DOUBLE, 1, dims_coord, &time_var);
|
||
handle_netcdf_error(status, "定义时间变量");
|
||
|
||
// 6. 定义数据变量(U和V分量)
|
||
int u10_var, v10_var;
|
||
int dims_data[3] = { time_dim, lat_dim, lon_dim };
|
||
status = nc_def_var(ncid, "u10", NC_DOUBLE, 3, dims_data, &u10_var);
|
||
handle_netcdf_error(status, "定义u10变量");
|
||
status = nc_def_var(ncid, "v10", NC_DOUBLE, 3, dims_data, &v10_var);
|
||
handle_netcdf_error(status, "定义v10变量");
|
||
|
||
// 7. 添加属性
|
||
std::string time_units = "hours since 1900-01-01 00:00:00.0";
|
||
status = nc_put_att_text(ncid, time_var, "units", time_units.length(), time_units.c_str());
|
||
handle_netcdf_error(status, "添加时间单位属性");
|
||
|
||
status = nc_put_att_text(ncid, lon_var, "units", 12, "degrees_east");
|
||
status = nc_put_att_text(ncid, lat_var, "units", 13, "degrees_north");
|
||
status = nc_put_att_text(ncid, u10_var, "units", 6, "m s**-1");
|
||
status = nc_put_att_text(ncid, v10_var, "units", 6, "m s**-1");
|
||
status = nc_put_att_text(ncid, u10_var, "long_name", 25, "10 metre U wind component");
|
||
status = nc_put_att_text(ncid, v10_var, "long_name", 25, "10 metre V wind component");
|
||
|
||
// 8. 结束定义模式
|
||
status = nc_enddef(ncid);
|
||
handle_netcdf_error(status, "结束定义模式");
|
||
|
||
// 9. 写入坐标数据
|
||
std::vector<double> lon_data = create_coordinate_array(info.minLon, info.maxLon, info.Width);
|
||
std::vector<double> lat_data = create_coordinate_array(info.minLat, info.maxLat, info.Height);
|
||
std::vector<double> era5_time = convert_to_era5_time(time_arr);
|
||
std::reverse(lat_data.begin(), lat_data.end());
|
||
status = nc_put_var_double(ncid, lon_var, lon_data.data());
|
||
handle_netcdf_error(status, "写入经度数据");
|
||
status = nc_put_var_double(ncid, lat_var, lat_data.data());
|
||
handle_netcdf_error(status, "写入纬度数据");
|
||
status = nc_put_var_double(ncid, time_var, era5_time.data());
|
||
handle_netcdf_error(status, "写入时间数据");
|
||
|
||
// 10. 写入风场数据
|
||
std::vector<double> u_data(info.Height * info.Width);
|
||
std::vector<double> v_data(info.Height * info.Width);
|
||
|
||
size_t start[3] = { 0, 0, 0 };
|
||
size_t count[3] = { 1, info.Height, info.Width };
|
||
|
||
for (int64_t t = 0; t < info.Num; ++t) {
|
||
if (t != tid) { continue; }
|
||
if (read_wind_data_file(input_bin_file, info, t, u_data, v_data)) {
|
||
start[0] = t;
|
||
status = nc_put_vara_double(ncid, u10_var, start, count, u_data.data());
|
||
handle_netcdf_error(status, "写入u10数据");
|
||
status = nc_put_vara_double(ncid, v10_var, start, count, v_data.data());
|
||
handle_netcdf_error(status, "写入v10数据");
|
||
|
||
if (t % 10 == 0) {
|
||
std::cout << "已处理 " << t + 1 << "/" << info.Num << " 个时间步" << std::endl;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 11. 关闭文件
|
||
status = nc_close(ncid);
|
||
handle_netcdf_error(status, "关闭文件");
|
||
|
||
std::cout << "转换完成!输出文件: " << output_nc_file << std::endl;
|
||
return true;
|
||
|
||
}
|
||
catch (const std::exception& e) {
|
||
std::cerr << "转换过程出错: " << e.what() << std::endl;
|
||
return false;
|
||
}
|
||
}
|