wps的domain转为shp矢量

wps的namelist制作、python出图和转矢量

简介

wps(WRF Preprocessing System)是中尺度数值天气预报系统WRF(Weather Research and Forecasting)的预处理系统。

wrf模型示意图

wps的安装地址在GitHub上:https://github.com/wrf-model/WPS

下载完成后,就可以进行WPS安装,教程请查看官网:https://www2.mmm.ucar.edu/wrf/OnLineTutorial/compilation_tutorial.php

在wps安装成功后,会得到三个编译软件分别为geogrid、ungrib和metgrid。分别的功能是:

  • geogrid处理下垫面数据;

  • ungrib是将不同格式的气象数据转为统一格式

  • metgrid会将气象数据统一"裁剪"下垫面数据的网格上。

这三个exe文件的控制参数都是由namelist.wps进行控制的。

我今天想介绍的主要内容为根据namelist.input反推domain的空间位置并导出shp矢量,其他东西只会简单介绍。

namelist参数含义

不要去看汉化后的博客,官网有详细解释的:https://www2.mmm.ucar.edu/wrf/users/namelist_best_prac_wps.html#io_form_geogrid

由于我们要根据namelist.wps反推domain的空间分布,由几个特别重要的的参数,官网没有详细解释,我这里做一下补充:

(1)每一个domain都是由格网组成的,每层的格网大小由dxdyparent_grid_ratio决定。

请注意,e_wee_sn代表的是格的端点数,它们如果为n,则对应的格子数目为n-1,如图所示(e_we不会只有3,只是距离):

(2)e_we和e_sn都是指在该层范围内分辨率下的格子数目,比如304就是d02每行拥有303个格子。


(3)i_parent_start是经度的指标,一般用X表示,数字代表的子域d02左下角和d01的左下角的x方向的水平方向格子数。特别需要注意,

i_parent_start所代表的格子数量是d01的,不是d02。

j_parent_start是纬度的指标,一般用Y标识,是垂直方向的格子数

domain坐标计算的原理

(1)d01层的平面坐标计算

ref_lon,ref_lat是d01的中心位置,此外,它还有个特殊的作用:在wps中所有网格都是在一个投影的平面坐标系中的,(ref_lon,ref_lat)代表了在平面坐标系下的原点(0,0)。如果我们以d01举例,求它的四个顶点的坐标,我们需要明确,在兰伯特投影下的平面坐标系单位是米,d01的格子横向长度为dx,d01的格子纵向长度为dy。现在我们就可以求出d01的四个顶点在投影的平面坐标系下的坐标。

(2)d02、d03层的平面坐标计算

上面一步,我们已经求得了d01的四个顶点的坐标值。

d02的坐标,我们需要根据d01的左下角的坐标求得。

由此,我们获得了d02的四个顶点在投影下的平面坐标。

在获得d02的左下角坐标之后,按照相同方法,我们可以获得d03的平面坐标。

自此,我们明白了wps的namelist.wps的平面坐标计算原理,开始写代码。计算坐标点的代码如下:

# 初始化网格域
def initialize_domains(e_we, e_sn, i_parent_start, j_parent_start, parent_id, parent_grid_ratio, dx, dy, ref_lat, ref_lon, truelat1, truelat2):
    domains = []

    # 坐标系详细信息
    # 兰伯特 坐标系信息
    proj_params = {
        'proj': 'lcc',
        'lat_1': truelat1,
        'lat_2': truelat2,
        'lat_0': ref_lat,
        'lon_0': ref_lon,
        'x_0': 0,
        'y_0': 0,
        'datum': 'WGS84'
    }

    # 创建平面坐标系
    p_lcc = Proj(proj_params)

    # 计算d01的中心点平面坐标系的坐标
    x_center, y_center = p_lcc(ref_lon, ref_lat)

    print("x_center, y_center",x_center, y_center)

    for i in range(len(e_we)):
        if parent_id[i] == 1:
            # 针对d01
            if i == 0:
                parent_dx = dx
                parent_dy = dy

                parent_ref_lon = x_center
                parent_ref_lat = y_center

                grid_ratio = parent_grid_ratio[i]
                # 计算d01的左下角坐标
                d01_start_x = x_center - ((e_we[i] - 1) / 2) * parent_dx
                d01_start_y = y_center - ((e_sn[i] - 1) / 2) * parent_dy

                # 计算d01的平面坐标
                domains.append(compute_boundaries(parent_ref_lat, parent_ref_lon, e_we[i], e_sn[i], dx, dy, i_parent_start[i], j_parent_start[i], parent_dx, parent_dy, d01_start_y, d01_start_x, grid_ratio))
            # 针对d02
            else:
                parent_domain_idx = parent_id[i] - 1
                grid_ratio = parent_grid_ratio[i]
                parent_dx = dx
                parent_dy = dy
                dx = dx / grid_ratio
                dy = dy / grid_ratio
                parent_ref_lat = domains[parent_domain_idx][2]
                parent_ref_lon = domains[parent_domain_idx][0]

                # 计算d02的左下角坐标
                d02_start_x = domains[parent_domain_idx][0] + (i_parent_start[i] - 1) * parent_dx
                d02_start_y = domains[parent_domain_idx][2] + (j_parent_start[i] - 1) * parent_dy

                # 计算d02的经纬度
                domains.append(compute_boundaries(parent_ref_lat, parent_ref_lon, e_we[i], e_sn[i], dx, dy, i_parent_start[i], j_parent_start[i], parent_dx, parent_dy, d02_start_y, d02_start_x, grid_ratio))
        else:
            # 针对d03
            parent_domain_idx = parent_id[i] - 1
            grid_ratio = parent_grid_ratio[i]

            parent_dx = domains[parent_domain_idx][6]
            parent_dy = domains[parent_domain_idx][7]

            parent_ref_lat = domains[parent_domain_idx][2]
            parent_ref_lon = domains[parent_domain_idx][0]
            # 计算d03的左下角坐标
            d03_start_x = domains[parent_domain_idx][0] + (i_parent_start[i] - 1) * parent_dx
            d03_start_y = domains[parent_domain_idx][2] + (j_parent_start[i] - 1) * parent_dy

            # 计算d03的经纬度
            domains.append(compute_boundaries(parent_ref_lat, parent_ref_lon, e_we[i], e_sn[i], dx / grid_ratio, dy / grid_ratio, i_parent_start[i], j_parent_start[i], parent_dx, parent_dy, d03_start_y, d03_start_x, grid_ratio))

    return domains, proj_params

# 计算网格的边界
def compute_boundaries(ref_lat, ref_lon, e_we, e_sn, dx, dy, i_start, j_start, parent_dx, parent_dy, d_start_y, d_start_x, grid_ratio):
    # 计算子网格的左下角坐标
    grid_start_lon = d_start_x
    grid_start_lat = d_start_y

    # 计算右上角坐标
    grid_end_lon = grid_start_lon + (e_we - 1) * dx
    grid_end_lat = grid_start_lat + (e_sn - 1) * dy

    # 各方向的坐标
    west = grid_start_lon
    south = grid_start_lat
    east = grid_end_lon
    north = grid_end_lat

    grid_center_lat = (south + north) / 2
    grid_center_lon = (west + east) / 2

    return west, east, south, north, grid_center_lat, grid_center_lon, dx, dy

python出图

# 绘制地图
def plot_map(domains, gdf_level1, gdf_level2, gdf_level3, truelat1, truelat2, stand_lon, proj_params):
    proj = ccrs.LambertConformal(central_longitude=stand_lon, standard_parallels=(truelat1, truelat2))

    fig, ax = plt.subplots(figsize=(10, 10), subplot_kw={'projection': proj})

    # 绘制shapefile背景
    gdf_level1.to_crs(proj).plot(ax=ax, edgecolor='blue', facecolor='none', linewidth=1)  # 蓝色边框,空心
    gdf_level2.to_crs(proj).plot(ax=ax, edgecolor='red', facecolor='none', linewidth=1)   # 红色边框,空心
    gdf_level3.to_crs(proj).plot(ax=ax, edgecolor='black', facecolor='#43A7EE', linewidth=1)  # 黑色边框,RGB(67,167,238)填充

    # 创建坐标系对象
    crs_wgs84 = CRS.from_epsg(4326)  # 使用 EPSG 代码 4326 表示 WGS84 地理坐标系
    crs_lcc = CRS(proj_params)
    transformer = Transformer.from_crs(crs_lcc, crs_wgs84, always_xy=True)

    # 绘制每个嵌套网格的范围
    for i, (west, east, south, north, _, _, _, _) in enumerate(domains):
        # 将网格边界转换为经纬度
        # 左下
        west_lon_ZUO, south_lat_ZUO = transformer.transform(west, south)
        # 左上
        west_lon_ZUO2, north_lat_ZUO2 = transformer.transform(west, north)
        # 右上
        east_lon_YOU2, north_lat_YOU2 = transformer.transform(east, north)
        # 右下
        east_lon_YOU, south_lat_YOU = transformer.transform(east, south)

        # 打印经纬度范围
        print(f"Domain {i+1} bounds (west, east, south, north):")
        print(f"Longitude: {west_lon_ZUO} to {east_lon_YOU2}")
        print(f"Latitude: {south_lat_ZUO} to {north_lat_ZUO2}")

        # 使用 Polygon 创建每个网格的多边形,按照逆时针顺序连接点
        vertices = [(west_lon_ZUO, south_lat_ZUO), (east_lon_YOU, south_lat_YOU), (east_lon_YOU2, north_lat_YOU2), (west_lon_ZUO2, north_lat_ZUO2)]
        polygon = Polygon(vertices)
        ax.add_geometries([polygon], crs=ccrs.PlateCarree(), edgecolor='red' if i == 0 else 'blue' if i == 1 else 'green', facecolor='none', linewidth=2, label=f'Domain {i+1}')
        
        # 计算标注的位置(使用多边形的右上角点)
        lon_label, lat_label = east_lon_YOU2, north_lat_YOU2

        # 添加标注,并调整标注的位置
        ax.text(lon_label, lat_label, f'd0{i+1}', color='red', fontsize=12, ha='right', va='top', transform=ccrs.PlateCarree())

    # 转换 d01 的边界坐标到地理坐标
    west_d01, south_d01 = transformer.transform(domains[0][0], domains[0][2])
    east_d01, north_d01 = transformer.transform(domains[0][1], domains[0][3])

    print("west_d01, south_d01, east_d01, north_d01", west_d01, south_d01, east_d01, north_d01)
    # 设置显示范围,在经度和纬度方向上各自添加边距
    lon_margin = (east_d01 - west_d01) * 0.1  # 经度方向上的边距为d01经度范围的10%
    lat_margin = (north_d01 - south_d01) * 0.1  # 纬度方向上的边距为d01纬度范围的10%
    ax.set_extent([west_d01 - lon_margin, east_d01 + lon_margin, south_d01 - lat_margin, north_d01 + lat_margin], crs=ccrs.PlateCarree())

    # 添加海岸线和网格线
    ax.gridlines(draw_labels=True)

    plt.title('WRF Domains')
    plt.show()

我们加入研究区的矢量如下,出图效果如下:

namelist转矢量shp

既然我们已经知道d01到d03的坐标点,按照逆时针把矢量点串联起来,获得shp矢量。

# 输出d01到d03范围为shp
def output_domains_to_shapefile(domains, proj_params, output_shapefile_path):
    # 创建一个空的 GeoDataFrame,用于存储域的范围
    gdf_domains = gpd.GeoDataFrame(columns=['geometry', 'domain_id'], crs="EPSG:4326")
    
    # 创建坐标系对象
    crs_wgs84 = CRS.from_epsg(4326)  # 使用 EPSG 代码 4326 表示 WGS84 地理坐标系
    crs_lcc = CRS(proj_params)
    transformer = Transformer.from_crs(crs_lcc, crs_wgs84, always_xy=True)

    # 绘制每个嵌套网格的范围并添加到 GeoDataFrame
    for i, (west, east, south, north, _, _, _, _) in enumerate(domains):
        # 将网格边界转换为经纬度
        # 左下
        west_lon_ZUO, south_lat_ZUO = transformer.transform(west, south)
        # 左上
        west_lon_ZUO2, north_lat_ZUO2 = transformer.transform(west, north)
        # 右上
        east_lon_YOU2, north_lat_YOU2 = transformer.transform(east, north)
        # 右下
        east_lon_YOU, south_lat_YOU = transformer.transform(east, south)

        # 使用 Polygon 创建每个网格的多边形,按照逆时针顺序连接点
        vertices = [(west_lon_ZUO, south_lat_ZUO), (east_lon_YOU, south_lat_YOU), (east_lon_YOU2, north_lat_YOU2), (west_lon_ZUO2, north_lat_ZUO2)]
        polygon = Polygon(vertices)
        
        # 创建一个临时的 GeoDataFrame
        temp_gdf = gpd.GeoDataFrame([{'geometry': polygon, 'domain_id': f'Domain {i+1}'}], crs="EPSG:4326")
        
        # 使用 pd.concat 将临时的 GeoDataFrame 添加到主要的 GeoDataFrame 中
        gdf_domains = pd.concat([gdf_domains, temp_gdf], ignore_index=True)

    # 保存为 shapefile
    gdf_domains.to_file(output_shapefile_path, driver='ESRI Shapefile')

特别注意,我们需要最后生成的shp是wgs84坐标系,所以需要把平面坐标转回为wgs84坐标系。

完整代码

import re
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import geopandas as gpd
import pandas as pd
from shapely.geometry import Polygon
from pyproj import Proj,transform
from pyproj import CRS, Transformer


# 提取单个参数的函数
def get_param(pattern, content, index=0):
    match = re.search(pattern, content)
    if match:
        return float(match.group(1))
    else:
        raise ValueError(f'Parameter {pattern} not found in namelist.wps')

# 提取多个参数的函数
def get_params(pattern, content):
    match = re.search(pattern, content)
    if match:
        return [int(x) for x in match.group(1).split(',')]
    else:
        raise ValueError(f'Parameter {pattern} not found in namelist.wps')

# 读取并解析namelist.wps文件
def parse_namelist(namelist_path):
    with open(namelist_path, 'r') as file:
        namelist_content = file.read()

    dx = get_param(r'dx\s*=\s*(\d+)', namelist_content)
    dy = get_param(r'dy\s*=\s*(\d+)', namelist_content)
    ref_lat = get_param(r'ref_lat\s*=\s*([-+]?\d*\.\d+|\d+)', namelist_content)
    ref_lon = get_param(r'ref_lon\s*=\s*([-+]?\d*\.\d+|\d+)', namelist_content)
    e_we = get_params(r'e_we\s*=\s*([\d,\s]+)', namelist_content)
    e_sn = get_params(r'e_sn\s*=\s*([\d,\s]+)', namelist_content)
    i_parent_start = get_params(r'i_parent_start\s*=\s*([\d,\s]+)', namelist_content)
    j_parent_start = get_params(r'j_parent_start\s*=\s*([\d,\s]+)', namelist_content)
    parent_id = get_params(r'parent_id\s*=\s*([\d,\s]+)', namelist_content)
    parent_grid_ratio = get_params(r'parent_grid_ratio\s*=\s*([\d,\s]+)', namelist_content)
    truelat1 = get_param(r'truelat1\s*=\s*([-+]?\d*\.\d+|\d+)', namelist_content)
    truelat2 = get_param(r'truelat2\s*=\s*([-+]?\d*\.\d+|\d+)', namelist_content)
    stand_lon = get_param(r'stand_lon\s*=\s*([-+]?\d*\.\d+|\d+)', namelist_content)

    return dx, dy, ref_lat, ref_lon, e_we, e_sn, i_parent_start, j_parent_start, parent_id, parent_grid_ratio, truelat1, truelat2, stand_lon

# 初始化网格域
def initialize_domains(e_we, e_sn, i_parent_start, j_parent_start, parent_id, parent_grid_ratio, dx, dy, ref_lat, ref_lon, truelat1, truelat2):
    domains = []

    # 坐标系详细信息
    # 兰伯特 坐标系信息
    proj_params = {
        'proj': 'lcc',
        'lat_1': truelat1,
        'lat_2': truelat2,
        'lat_0': ref_lat,
        'lon_0': ref_lon,
        'x_0': 0,
        'y_0': 0,
        'datum': 'WGS84'
    }

    # 创建平面坐标系
    p_lcc = Proj(proj_params)

    # 计算d01的中心点平面坐标系的坐标
    x_center, y_center = p_lcc(ref_lon, ref_lat)

    print("x_center, y_center",x_center, y_center)

    for i in range(len(e_we)):
        if parent_id[i] == 1:
            # 针对d01
            if i == 0:
                parent_dx = dx
                parent_dy = dy

                parent_ref_lon = x_center
                parent_ref_lat = y_center

                grid_ratio = parent_grid_ratio[i]
                # 计算d01的左下角坐标
                d01_start_x = x_center - ((e_we[i] - 1) / 2) * parent_dx
                d01_start_y = y_center - ((e_sn[i] - 1) / 2) * parent_dy

                # 计算d01的平面坐标
                domains.append(compute_boundaries(parent_ref_lat, parent_ref_lon, e_we[i], e_sn[i], dx, dy, i_parent_start[i], j_parent_start[i], parent_dx, parent_dy, d01_start_y, d01_start_x, grid_ratio))
            # 针对d02
            else:
                parent_domain_idx = parent_id[i] - 1
                grid_ratio = parent_grid_ratio[i]
                parent_dx = dx
                parent_dy = dy
                dx = dx / grid_ratio
                dy = dy / grid_ratio
                parent_ref_lat = domains[parent_domain_idx][2]
                parent_ref_lon = domains[parent_domain_idx][0]

                # 计算d02的左下角坐标
                d02_start_x = domains[parent_domain_idx][0] + (i_parent_start[i] - 1) * parent_dx
                d02_start_y = domains[parent_domain_idx][2] + (j_parent_start[i] - 1) * parent_dy

                # 计算d02的经纬度
                domains.append(compute_boundaries(parent_ref_lat, parent_ref_lon, e_we[i], e_sn[i], dx, dy, i_parent_start[i], j_parent_start[i], parent_dx, parent_dy, d02_start_y, d02_start_x, grid_ratio))
        else:
            # 针对d03
            parent_domain_idx = parent_id[i] - 1
            grid_ratio = parent_grid_ratio[i]

            parent_dx = domains[parent_domain_idx][6]
            parent_dy = domains[parent_domain_idx][7]

            parent_ref_lat = domains[parent_domain_idx][2]
            parent_ref_lon = domains[parent_domain_idx][0]
            # 计算d03的左下角坐标
            d03_start_x = domains[parent_domain_idx][0] + (i_parent_start[i] - 1) * parent_dx
            d03_start_y = domains[parent_domain_idx][2] + (j_parent_start[i] - 1) * parent_dy

            # 计算d03的经纬度
            domains.append(compute_boundaries(parent_ref_lat, parent_ref_lon, e_we[i], e_sn[i], dx / grid_ratio, dy / grid_ratio, i_parent_start[i], j_parent_start[i], parent_dx, parent_dy, d03_start_y, d03_start_x, grid_ratio))

    return domains, proj_params

# 计算网格的边界
def compute_boundaries(ref_lat, ref_lon, e_we, e_sn, dx, dy, i_start, j_start, parent_dx, parent_dy, d_start_y, d_start_x, grid_ratio):
    # 计算子网格的左下角坐标
    grid_start_lon = d_start_x
    grid_start_lat = d_start_y

    # 计算右上角坐标
    grid_end_lon = grid_start_lon + (e_we - 1) * dx
    grid_end_lat = grid_start_lat + (e_sn - 1) * dy

    # 各方向的坐标
    west = grid_start_lon
    south = grid_start_lat
    east = grid_end_lon
    north = grid_end_lat

    grid_center_lat = (south + north) / 2
    grid_center_lon = (west + east) / 2

    return west, east, south, north, grid_center_lat, grid_center_lon, dx, dy

# 绘制地图
def plot_map(domains, gdf_level1, gdf_level2, gdf_level3, truelat1, truelat2, stand_lon, proj_params):
    proj = ccrs.LambertConformal(central_longitude=stand_lon, standard_parallels=(truelat1, truelat2))

    fig, ax = plt.subplots(figsize=(10, 10), subplot_kw={'projection': proj})

    # 绘制shapefile背景
    gdf_level1.to_crs(proj).plot(ax=ax, edgecolor='blue', facecolor='none', linewidth=1)  # 蓝色边框,空心
    gdf_level2.to_crs(proj).plot(ax=ax, edgecolor='red', facecolor='none', linewidth=1)   # 红色边框,空心
    gdf_level3.to_crs(proj).plot(ax=ax, edgecolor='black', facecolor='#43A7EE', linewidth=1)  # 黑色边框,RGB(67,167,238)填充

    # 创建坐标系对象
    crs_wgs84 = CRS.from_epsg(4326)  # 使用 EPSG 代码 4326 表示 WGS84 地理坐标系
    crs_lcc = CRS(proj_params)
    transformer = Transformer.from_crs(crs_lcc, crs_wgs84, always_xy=True)

    # 绘制每个嵌套网格的范围
    for i, (west, east, south, north, _, _, _, _) in enumerate(domains):
        # 将网格边界转换为经纬度
        # 左下
        west_lon_ZUO, south_lat_ZUO = transformer.transform(west, south)
        # 左上
        west_lon_ZUO2, north_lat_ZUO2 = transformer.transform(west, north)
        # 右上
        east_lon_YOU2, north_lat_YOU2 = transformer.transform(east, north)
        # 右下
        east_lon_YOU, south_lat_YOU = transformer.transform(east, south)

        # 打印经纬度范围
        print(f"Domain {i+1} bounds (west, east, south, north):")
        print(f"Longitude: {west_lon_ZUO} to {east_lon_YOU2}")
        print(f"Latitude: {south_lat_ZUO} to {north_lat_ZUO2}")

        # 使用 Polygon 创建每个网格的多边形,按照逆时针顺序连接点
        vertices = [(west_lon_ZUO, south_lat_ZUO), (east_lon_YOU, south_lat_YOU), (east_lon_YOU2, north_lat_YOU2), (west_lon_ZUO2, north_lat_ZUO2)]
        polygon = Polygon(vertices)
        ax.add_geometries([polygon], crs=ccrs.PlateCarree(), edgecolor='red' if i == 0 else 'blue' if i == 1 else 'green', facecolor='none', linewidth=2, label=f'Domain {i+1}')
        
        # 计算标注的位置(使用多边形的右上角点)
        lon_label, lat_label = east_lon_YOU2, north_lat_YOU2

        # 添加标注,并调整标注的位置
        ax.text(lon_label, lat_label, f'd0{i+1}', color='red', fontsize=12, ha='right', va='top', transform=ccrs.PlateCarree())

    # 转换 d01 的边界坐标到地理坐标
    west_d01, south_d01 = transformer.transform(domains[0][0], domains[0][2])
    east_d01, north_d01 = transformer.transform(domains[0][1], domains[0][3])

    print("west_d01, south_d01, east_d01, north_d01", west_d01, south_d01, east_d01, north_d01)
    # 设置显示范围,在经度和纬度方向上各自添加边距
    lon_margin = (east_d01 - west_d01) * 0.1  # 经度方向上的边距为d01经度范围的10%
    lat_margin = (north_d01 - south_d01) * 0.1  # 纬度方向上的边距为d01纬度范围的10%
    ax.set_extent([west_d01 - lon_margin, east_d01 + lon_margin, south_d01 - lat_margin, north_d01 + lat_margin], crs=ccrs.PlateCarree())

    # 添加海岸线和网格线
    ax.gridlines(draw_labels=True)

    plt.title('WRF Domains')
    plt.show()

# 输出d01到d03范围为shp
def output_domains_to_shapefile(domains, proj_params, output_shapefile_path):
    # 创建一个空的 GeoDataFrame,用于存储域的范围
    gdf_domains = gpd.GeoDataFrame(columns=['geometry', 'domain_id'], crs="EPSG:4326")
    
    # 创建坐标系对象
    crs_wgs84 = CRS.from_epsg(4326)  # 使用 EPSG 代码 4326 表示 WGS84 地理坐标系
    crs_lcc = CRS(proj_params)
    transformer = Transformer.from_crs(crs_lcc, crs_wgs84, always_xy=True)

    # 绘制每个嵌套网格的范围并添加到 GeoDataFrame
    for i, (west, east, south, north, _, _, _, _) in enumerate(domains):
        # 将网格边界转换为经纬度
        # 左下
        west_lon_ZUO, south_lat_ZUO = transformer.transform(west, south)
        # 左上
        west_lon_ZUO2, north_lat_ZUO2 = transformer.transform(west, north)
        # 右上
        east_lon_YOU2, north_lat_YOU2 = transformer.transform(east, north)
        # 右下
        east_lon_YOU, south_lat_YOU = transformer.transform(east, south)

        # 使用 Polygon 创建每个网格的多边形,按照逆时针顺序连接点
        vertices = [(west_lon_ZUO, south_lat_ZUO), (east_lon_YOU, south_lat_YOU), (east_lon_YOU2, north_lat_YOU2), (west_lon_ZUO2, north_lat_ZUO2)]
        polygon = Polygon(vertices)
        
        # 创建一个临时的 GeoDataFrame
        temp_gdf = gpd.GeoDataFrame([{'geometry': polygon, 'domain_id': f'Domain {i+1}'}], crs="EPSG:4326")
        
        # 使用 pd.concat 将临时的 GeoDataFrame 添加到主要的 GeoDataFrame 中
        gdf_domains = pd.concat([gdf_domains, temp_gdf], ignore_index=True)

    # 保存为 shapefile
    gdf_domains.to_file(output_shapefile_path, driver='ESRI Shapefile')

def main():
    # 读取namelist.wps文件
    read_path = r"E:\ruiduobao\namelis设置\namelist.wps"
    dx, dy, ref_lat, ref_lon, e_we, e_sn, i_parent_start, j_parent_start, parent_id, parent_grid_ratio, truelat1, truelat2, stand_lon = parse_namelist(read_path)

    # 初始化网格域
    domains, proj_params = initialize_domains(e_we, e_sn, i_parent_start, j_parent_start, parent_id, parent_grid_ratio, dx, dy, ref_lat, ref_lon, truelat1, truelat2)

    # 读取shapefile
    shapefile_path_level1 = r'E:\ruiduobao\数据和代码\行政区划\jiangsu.shp'
    shapefile_path_level2 = r'E:\ruiduobao\数据和代码\行政区划\xuzhou.shp'
    shapefile_path_level3 = r'E:\ruiduobao\数据和代码\行政区划\xuzhouxian.shp'

    # 加载shapefile到 GeoDataFrame
    gdf_level1 = gpd.read_file(shapefile_path_level1)
    gdf_level2 = gpd.read_file(shapefile_path_level2)
    gdf_level3 = gpd.read_file(shapefile_path_level3)

    # 绘制地图
    plot_map(domains, gdf_level1, gdf_level2, gdf_level3, truelat1, truelat2, stand_lon, proj_params)

    # 输出d01到d03范围为shp
    output_shapefile_path = r'E:\ruiduobao\行政区划\wrf_domains_平面.shp'
    output_domains_to_shapefile(domains, proj_params, output_shapefile_path)

if __name__ == '__main__':
    main()

最后,我们把生成的namelist.wps的矢量放到GIS软件中,就可以任意编辑了:

总结

这个代码看起来很简单,但我实际上搞了快两天才弄懂里面的原理,尴尬,故写一篇技术博客方便以后自己查阅。我主要遇到以下问题:

(1)一开始我是用wgs84的经纬度去计算的各个domain的空间位置的,但实际上会有偏移,因为每度随着纬度的不同是会变化的,需要放到平面坐标系中才会有正确的结果。

(2)我刚开始是计算每一个domain的中心点,但实际上这是比较傻的方法,因为i_parent_start等是从左下角开始计数的。

参考

https://github.com/wrf-model/WPS

https://www2.mmm.ucar.edu/wrf/OnLineTutorial/compilation_tutorial.php

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/751529.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

注册中心不知选哪个?Zookeeper、Eureka、Nacos、Consul和Etcd 5种全方位剖析对比

本文给大家讲解 5 种常用的注册中心,对比其流程和原理,无论是面试还是技术选型,都非常有帮助。 对于注册中心,在写这篇文章前,我其实只对 ETCD 有比较深入的了解,但是对于 Zookeeper 和其他的注册中心了解甚…

pytorch统计学分布

1、pytorch统计学函数 import torcha torch.rand(2,2) print(a) print(torch.sum(a, dim0)) print(torch.mean(a, dim0)) print(torch.prod(a, dim0))print(torch.argmax(a, dim0)) print(torch.argmin(a, dim0)) print(torch.std(a)) print(torch.var(a)) print(torch.median…

AI进阶指南第四课,大模型优缺点研究?

在上一篇文章中,我主要探讨了LM模型与企业级模型的融合。 但是,在文末对于具体的大模型优缺点只是简单地说明了一下,并不细致。 因此,在这一节,我将更为细致地说明一下大模型的优缺点。 一,隐私安全 将L…

Python输入与输出基础

Python输入与输出基础 引言 Python是一种非常直观且功能强大的编程语言,它允许用户轻松地处理输入和输出操作。无论是从用户那里获取数据,还是将结果展示给用户,Python都提供了简单易用的函数和方法。 一、输入数据 在Python中&#xff0c…

UWB:DS-TWR( Double-sided two-way ranging)双边测距公式推导:为啥是乘法?

UWB DS-TWR( Double-sided two-way ranging)双边测距为啥是乘法?? 公式: 我们先看单边 Single-Sided Two-Way Ranging (SS-TWR) 单边很好理解。 symmetric double-sided TWR (SDS-TWR)对称的双边测距 再看双边 Trou…

LeetCode热题100——最长连续序列

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 class Solution(object):def longestConsecutive(self, nums):""":t…

【MAVEN学习 | 第2篇】Maven工程创建及核心功能

文章目录 一. 基于IDEA的Maven工程创建1.1 Maven工程GAVP属性(1)GroupID 格式(2)ArtifactID 格式(3)Version版本号格式(4)Packaging定义规则 1.2 IDEA构建Maven JavaSE工程1.3 IDEA构…

kettle使用手册 安装9.0版本 建议设置为英语

0.新建转换的常用组件 0. Generate rows 定义一个字符串 name value就是字符串的值 0.1 String operations 字段转大写 去空格 1. Json input 来源于一个json文件 1.json 或mq接收到的data内容是json字符串 2. Json output 定义Jsonbloc值为 data, 左侧Fieldname是数据库…

VS2022(Visual Studio 2022)最新安装教程

1、下载 1、下载地址 - 官网地址:下载 Visual Studio Tools - 免费安装 Windows、Mac、Linux - 根据自己的电脑的 【操作系统】 灵活选择。 2、安装包 【此处为Windows系统安装包】 2、安装 1、打开软件 - 右击【以管理员身份打开】, 2、准备配置 …

昇思25天学习打卡营第03天|张量Tensor

何为张量? 张量(Tensor)是一个可用来表示在一些矢量、标量和其他张量之间的线性关系的多线性函数,这些线性关系的基本例子有内积、外积、线性映射以及笛卡儿积。其坐标在 𝑛维空间内,有  𝑛&a…

机器人控制系列教程之URDF文件语法介绍

前两期推文:机器人控制系列教程之动力学建模(1)、机器人控制系列教程之动力学建模(2),我们主要从数学的角度介绍了机器人的动力学建模的方式,随着机器人技术的不断发展,机器人建模成为了机器人系统设计中的一项关键任务。URDF&…

聚合项目学习

首先建立一个总的工程目录,里边后期会有我们的父工程、基础工程(继承父工程)、业务工程(依赖基础工程)等模块 1、在总工程目录中(open一个空的文件夹),首先建立一个父工程模块(通过spring init…

地铁中的CAN通信--地铁高效安全运转原理

目前地铁采用了自动化的技术来实现控制,有ATC(列车自动控制)系统可以实现列车自动驾驶、自动跟踪、自动调度;SCADA(供电系统管理自动化)系统可以实现主变电所、牵引变电所、降压变电所设备系统的遥控、遥信、遥测;BAS(环境监控系统)和FAS(火灾报警系统)可以实现车站…

AS-V1000外部设备管理介绍(国标GB28181设备管理,可以管理的国标设备包括DVR/NVR、IPC、第三方国标28181平台)

目录 一、概述 1、视频监控平台介绍 2、外部设备定义(接入的国标设备) 二、外部设备管理 2.1 外部设备添加 (1)设备侧的配置 (2)平台侧的配置 2.2 外部设备信息的修改 三、外部通道管理 3.1 外部…

【技术追踪】SDSeg:医学图像的 Stable Diffusion 分割(MICCAI-2024)

这医学图像分割领域啊,终究还是被 Stable Diffusion 闯进去了~ SDSeg:第一个基于 Stable Diffusion 的 latent 扩散医学图像分割模型,在五个不同医学影像模态的基准数据集上超越了现有的最先进方法~ 论文:Stable Diffusion Segmen…

当设备树中出现多个同一节点的处理办法

当设备树中出现多个同一节点的处理办法 1.同一文件下有多个节点不同设备树调用同一节点需要#include "xxx.dtsi"3,vscode快速搜索文件 ctrlshiftp 去掉> 1.同一文件下有多个节点 覆盖规则: 同一层次的节点,后面的会覆盖前面的节点 memory…

如何在浏览器中查看网页的HTML源代码?

如何在浏览器中查看网页的HTML源代码? 浏览html网页,查看其源代码,可以帮助我们了解该版网页的信息以及架构,每个浏览器都是允许用户查看他们访问的任何网页的HTML源代码的。以下编程狮小师妹就介绍几个常见浏览器的查看网页 HTM…

STL中的迭代器模式:将算法与数据结构分离

目录 1.概述 2.容器类 2.1.序列容器 2.2.关联容器 2.3.容器适配器 2.4.数组 3.迭代器 4.重用标准迭代器 5.总结 1.概述 在之前,我们讲了迭代器设计模式,分析了它的结构、角色以及优缺点: 设计模式之迭代器模式-CSDN博客 在 STL 中&a…

Jenkins教程-10-发送飞书测试报告通知

上一小节我们学习了发送企业微信测试报告通知的方法,本小节我们讲解一下发送飞书测试报告通知的方法。 1、自动化用例执行完后,使用pytest_terminal_summary钩子函数收集测试结果,存入本地status.txt文件中,供Jenkins调用 conft…

“山寨版”《草料二维码》

背景 之前浏览过草料二维码的网站,他的二维码美化功能很强大💪,可以分别自定义码眼和码点的形状和颜色! 碰巧之前写过一个 npm 插件 qrcode-with-logos, 用于前端生成带 logo 的二维码。 而且在 github 的 issues 里有外国友人…