20241218 update proj merc
This commit is contained in:
229
proj_info.py
229
proj_info.py
@ -6,8 +6,11 @@ import logging
|
|||||||
更新记录:
|
更新记录:
|
||||||
2022-09-23 11:59:01 Sola v1 编写源代码, 修正set_lc代码错误的问题
|
2022-09-23 11:59:01 Sola v1 编写源代码, 修正set_lc代码错误的问题
|
||||||
2024-12-16 09:54:40 Sola v2 增加变量检测的内容
|
2024-12-16 09:54:40 Sola v2 增加变量检测的内容
|
||||||
|
2024-12-17 15:39:45 Sola v3 增加墨卡托投影
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
logging.basicConfig(format='[%(asctime)s][%(levelname)s]: %(message)s',
|
||||||
|
level=logging.DEBUG, datefmt='%Y-%m-%dT%H:%M:%S %Z')
|
||||||
EARTH_RADIUS_M = 6370000.
|
EARTH_RADIUS_M = 6370000.
|
||||||
|
|
||||||
class proj_info(object):
|
class proj_info(object):
|
||||||
@ -19,7 +22,7 @@ class proj_info(object):
|
|||||||
nxmax=None, hemi=None, cone=None, polei=None, polej=None,
|
nxmax=None, hemi=None, cone=None, polei=None, polej=None,
|
||||||
rsw=None, knowni=None, knownj=None, re_m=EARTH_RADIUS_M,
|
rsw=None, knowni=None, knownj=None, re_m=EARTH_RADIUS_M,
|
||||||
init=False, wrap=False, rho0=None, nc=None, bigc=None, comp_ll=False,
|
init=False, wrap=False, rho0=None, nc=None, bigc=None, comp_ll=False,
|
||||||
gauss_lat=None) -> None:
|
gauss_lat=None, nx=None, ny=None) -> None:
|
||||||
self.code = code
|
self.code = code
|
||||||
self.lat1 = lat1 # SW latitude (1,1) in degrees (-90->90N) 格点(1, 1)纬度, 西南角, 度
|
self.lat1 = lat1 # SW latitude (1,1) in degrees (-90->90N) 格点(1, 1)纬度, 西南角, 度
|
||||||
self.lon1 = lon1 # SW longitude (1,1) in degrees (-180->180E) 格点(1, 1)经度, 西南角, 度
|
self.lon1 = lon1 # SW longitude (1,1) in degrees (-180->180E) 格点(1, 1)经度, 西南角, 度
|
||||||
@ -56,7 +59,12 @@ class proj_info(object):
|
|||||||
self.bigc = bigc
|
self.bigc = bigc
|
||||||
self.comp_ll = comp_ll
|
self.comp_ll = comp_ll
|
||||||
self.gauss_lat = gauss_lat
|
self.gauss_lat = gauss_lat
|
||||||
|
self.nx = nx
|
||||||
|
self.ny = ny
|
||||||
|
|
||||||
|
if self.knowni is None and self.knownj is None:
|
||||||
|
self.knowni = (self.nx + 1) / 2
|
||||||
|
self.knownj = (self.ny + 1) / 2
|
||||||
if self.lat1:
|
if self.lat1:
|
||||||
if abs(self.lat1) > 90:
|
if abs(self.lat1) > 90:
|
||||||
logging.error("Latitude of origin corner required as follows: -90N <= lat1 < = 90.N")
|
logging.error("Latitude of origin corner required as follows: -90N <= lat1 < = 90.N")
|
||||||
@ -85,6 +93,29 @@ class proj_info(object):
|
|||||||
self.hemi = 1
|
self.hemi = 1
|
||||||
self.rebydx = self.re_m / self.dx # 地球半径除以网格距
|
self.rebydx = self.re_m / self.dx # 地球半径除以网格距
|
||||||
|
|
||||||
|
def grid_id_float(self, lon, lat):
|
||||||
|
"""返回以0为开始的下标"""
|
||||||
|
i,j = self.llij(lon, lat)
|
||||||
|
return i - 1, j - 1
|
||||||
|
|
||||||
|
def grid_ids_float(self, lon_array, lat_array):
|
||||||
|
"""返回以0为开始的下标数组"""
|
||||||
|
i_array, j_array = self.llij(lon_array, lat_array)
|
||||||
|
return i_array - 1, j_array - 1
|
||||||
|
|
||||||
|
def grid_lonlat(self, ix, iy):
|
||||||
|
"""返回对应网格(以0为下标开始)的经纬度"""
|
||||||
|
lon, lat = self.ijll(ix + 1, iy + 1)
|
||||||
|
return lon, lat
|
||||||
|
|
||||||
|
def grid_lonlats(self, ix_array, iy_array):
|
||||||
|
"""返回对应网格数组(以0为下标开始)的经纬度数组"""
|
||||||
|
lon_array, lat_array = self.ijll(ix_array + 1, iy_array + 1)
|
||||||
|
return lon_array, lat_array
|
||||||
|
|
||||||
|
def transform_point(self, lon, lat, proj_useless=None):
|
||||||
|
"""返回对应经纬度坐标的网格坐标(m)"""
|
||||||
|
pass
|
||||||
|
|
||||||
class proj_LC(proj_info):
|
class proj_LC(proj_info):
|
||||||
"""
|
"""
|
||||||
@ -116,25 +147,11 @@ class proj_LC(proj_info):
|
|||||||
if truelat1 is None or truelat2 is None or lat1 is None or lon1 is None\
|
if truelat1 is None or truelat2 is None or lat1 is None or lon1 is None\
|
||||||
or nx is None or ny is None or stdlon is None or dx is None:
|
or nx is None or ny is None or stdlon is None or dx is None:
|
||||||
print('[ERROR] cannot generate proj!')
|
print('[ERROR] cannot generate proj!')
|
||||||
if abs(lat1) > 90 or dx <= 0 or truelat1 > 90:
|
|
||||||
pass
|
|
||||||
dummy_lon1 = (lon1 + 180) % 360 - 180 # 限制经度范围
|
|
||||||
dummy_stdlon = (stdlon + 180) % 360 - 180 # 限制中央经线范围
|
|
||||||
if knowni is None and knownj is None:
|
|
||||||
knowni = (nx + 1) / 2
|
|
||||||
knownj = (ny + 1) / 2
|
|
||||||
if dy is None: # 设置dy, 如果dy不存在, 则利用dx给定
|
|
||||||
dy = dx
|
|
||||||
if truelat1 < 0: # 所在半球, 1为北半球, -1为南半球
|
|
||||||
hemi = -1
|
|
||||||
else:
|
|
||||||
hemi = 1
|
|
||||||
if abs(truelat2) > 90: # 如果标准纬线2超过范围, 则用标准纬线1赋值
|
if abs(truelat2) > 90: # 如果标准纬线2超过范围, 则用标准纬线1赋值
|
||||||
truelat2 = truelat1
|
truelat2 = truelat1
|
||||||
super().__init__(code=code, lat1=lat1, lon1=dummy_lon1, dx=dx, dy=dy,
|
super().__init__(code=code, lat1=lat1, lon1=lon1, dx=dx, dy=dy,
|
||||||
stdlon=dummy_stdlon, truelat1=truelat1, truelat2=truelat2, hemi=hemi,
|
stdlon=stdlon, truelat1=truelat1, truelat2=truelat2,
|
||||||
knowni=knowni, knownj=knownj, re_m=re_m) # 初始化各变量
|
knowni=knowni, knownj=knownj, re_m=re_m, nx=nx, ny=ny) # 初始化各变量
|
||||||
self.rebydx = re_m / dx # 地球半径除以网格距
|
|
||||||
self.set_lc() # 计算其他变量
|
self.set_lc() # 计算其他变量
|
||||||
self.check_init() # 确认是否所有变量都计算完毕
|
self.check_init() # 确认是否所有变量都计算完毕
|
||||||
|
|
||||||
@ -167,23 +184,7 @@ class proj_LC(proj_info):
|
|||||||
else:
|
else:
|
||||||
self.cone = sin(radians(abs(self.truelat1)))
|
self.cone = sin(radians(abs(self.truelat1)))
|
||||||
|
|
||||||
def llij_lc(self, lon, lat):
|
def llij(self, lon, lat):
|
||||||
"""通过经纬度计算坐标"""
|
|
||||||
if not self.init:
|
|
||||||
print('[ERROR] proj cannot use!')
|
|
||||||
deltalon = lon - self.stdlon # 计算经度与中央经线差
|
|
||||||
deltalon = (deltalon + 180) % 360 - 180 # 限定范围 -180~180
|
|
||||||
ctl1r = cos(radians(self.truelat1)) # 剩下就看不懂了
|
|
||||||
rm = self.rebydx * ctl1r / self.cone * (tan(radians(90*self.hemi \
|
|
||||||
- lat)/2)/tan(radians(90*self.hemi - self.truelat1)/2))**self.cone
|
|
||||||
arg = self.cone * radians(deltalon)
|
|
||||||
i = self.polei + self.hemi * rm * sin(arg)
|
|
||||||
j = self.polej - rm * cos(arg)
|
|
||||||
i = self.hemi * i
|
|
||||||
j = self.hemi * j
|
|
||||||
return i, j
|
|
||||||
|
|
||||||
def llijs_lc(self, lon, lat):
|
|
||||||
"""通过经纬度序列计算坐标"""
|
"""通过经纬度序列计算坐标"""
|
||||||
if not self.init:
|
if not self.init:
|
||||||
print('[ERROR] proj cannot use!')
|
print('[ERROR] proj cannot use!')
|
||||||
@ -199,32 +200,7 @@ class proj_LC(proj_info):
|
|||||||
j = self.hemi * j
|
j = self.hemi * j
|
||||||
return i, j
|
return i, j
|
||||||
|
|
||||||
def ijll_lc(self, i, j):
|
def ijll(self, i, j):
|
||||||
"""通过坐标计算经纬度"""
|
|
||||||
if not self.init:
|
|
||||||
print('[ERROR] proj cannot use!')
|
|
||||||
chi1 = radians(90 - self.hemi * self.truelat1)
|
|
||||||
chi2 = radians(90 - self.hemi * self.truelat2)
|
|
||||||
inew = self.hemi * i
|
|
||||||
jnew = self.hemi * j
|
|
||||||
xx = inew - self.polei
|
|
||||||
yy = self.polej - jnew
|
|
||||||
r2 = xx**2 + yy**2
|
|
||||||
r = sqrt(r2) / self.rebydx
|
|
||||||
if r2 == 0.:
|
|
||||||
lat = self.hemi * 90
|
|
||||||
lon = self.stdlon
|
|
||||||
else:
|
|
||||||
lon = self.stdlon + degrees(atan2(self.hemi*xx, yy))/self.cone
|
|
||||||
lon = (lon + 180) % 360 - 180
|
|
||||||
if chi1 == chi2:
|
|
||||||
chi = 2 * atan((r/tan(chi1))**(1/self.cone) * tan(chi1*0.5))
|
|
||||||
else:
|
|
||||||
chi = 2 * atan((r*self.cone/sin(chi1))**(1/self.cone) * tan(chi1*0.5))
|
|
||||||
lat = (90 - degrees(chi)) * self.hemi
|
|
||||||
return lon, lat
|
|
||||||
|
|
||||||
def ijlls_lc(self, i, j):
|
|
||||||
"""
|
"""
|
||||||
通过坐标计算经纬度
|
通过坐标计算经纬度
|
||||||
2022-09-28 18:22:26 Sola 修正计算chi时, xx, yy未进行筛选的问题
|
2022-09-28 18:22:26 Sola 修正计算chi时, xx, yy未进行筛选的问题
|
||||||
@ -235,8 +211,8 @@ class proj_LC(proj_info):
|
|||||||
chi2 = np.radians(90 - self.hemi * self.truelat2)
|
chi2 = np.radians(90 - self.hemi * self.truelat2)
|
||||||
inew = self.hemi * i
|
inew = self.hemi * i
|
||||||
jnew = self.hemi * j
|
jnew = self.hemi * j
|
||||||
xx = inew - self.polei
|
xx = np.array(inew - self.polei)
|
||||||
yy = self.polej - jnew
|
yy = np.array(self.polej - jnew)
|
||||||
r2 = xx**2 + yy**2
|
r2 = xx**2 + yy**2
|
||||||
r = np.sqrt(r2) / self.rebydx
|
r = np.sqrt(r2) / self.rebydx
|
||||||
lon = np.zeros(r2.shape)
|
lon = np.zeros(r2.shape)
|
||||||
@ -261,39 +237,20 @@ class proj_LC(proj_info):
|
|||||||
else:
|
else:
|
||||||
self.init = True
|
self.init = True
|
||||||
|
|
||||||
def grid_id_float(self, lon, lat):
|
|
||||||
"""返回以0为开始的下标"""
|
|
||||||
i,j = self.llij_lc(lon, lat)
|
|
||||||
return i - 1, j - 1
|
|
||||||
|
|
||||||
def grid_ids_float(self, lon_array, lat_array):
|
|
||||||
"""返回以0为开始的下标数组"""
|
|
||||||
i_array, j_array = self.llijs_lc(lon_array, lat_array)
|
|
||||||
return i_array - 1, j_array - 1
|
|
||||||
|
|
||||||
def grid_lonlat(self, ix, iy):
|
|
||||||
"""返回对应网格(以0为下标开始)的经纬度"""
|
|
||||||
lon, lat = self.ijll_lc(ix + 1, iy + 1)
|
|
||||||
return lon, lat
|
|
||||||
|
|
||||||
def grid_lonlats(self, ix_array, iy_array):
|
|
||||||
"""返回对应网格数组(以0为下标开始)的经纬度数组"""
|
|
||||||
lon_array, lat_array = self.ijlls_lc(ix_array + 1, iy_array + 1)
|
|
||||||
return lon_array, lat_array
|
|
||||||
|
|
||||||
def transform_point(self, lon, lat, proj_useless=None):
|
def transform_point(self, lon, lat, proj_useless=None):
|
||||||
"""返回对应经纬度坐标的网格坐标(m)"""
|
"""返回对应经纬度坐标的网格坐标(m)"""
|
||||||
ix, iy = self.llij_lc(lon, lat)
|
ix, iy = self.llij(lon, lat)
|
||||||
cx, cy1 = self.llij_lc(self.stdlon, self.truelat1)
|
cx, cy1 = self.llij(self.stdlon, self.truelat1)
|
||||||
cx, cy2 = self.llij_lc(self.stdlon, self.truelat2)
|
cx, cy2 = self.llij(self.stdlon, self.truelat2)
|
||||||
cy = (cy1 + cy2) / 2
|
cy = (cy1 + cy2) / 2
|
||||||
return (ix - cx) * self.dx, (iy - cy) * self.dy
|
return (ix - cx) * self.dx, (iy - cy) * self.dy
|
||||||
|
|
||||||
|
|
||||||
class proj_MERC(proj_info):
|
class proj_MERC(proj_info):
|
||||||
"""
|
"""
|
||||||
参考WPS源码中的proj_LC改写, 因为WRF计算得到的网格与cartopy的不同
|
参考WPS源码中的proj_MERC改写, 因为WRF计算得到的网格与cartopy的不同
|
||||||
更新记录:
|
更新记录:
|
||||||
2022-09-22 22:07:51 Sola 编写源代码
|
2024-12-17 15:12:51 Sola 编写源代码
|
||||||
"""
|
"""
|
||||||
def __init__(self, code='PROJ_MERC', truelat1=None, lat1=None,
|
def __init__(self, code='PROJ_MERC', truelat1=None, lat1=None,
|
||||||
lon1=None, knowni=None, knownj=None, stdlon=None, dx=None,
|
lon1=None, knowni=None, knownj=None, stdlon=None, dx=None,
|
||||||
@ -319,54 +276,72 @@ class proj_MERC(proj_info):
|
|||||||
if truelat1 is None is None or lat1 is None or lon1 is None\
|
if truelat1 is None is None or lat1 is None or lon1 is None\
|
||||||
or nx is None or ny is None or dx is None:
|
or nx is None or ny is None or dx is None:
|
||||||
print('[ERROR] cannot generate proj!')
|
print('[ERROR] cannot generate proj!')
|
||||||
if abs(lat1) > 90 or dx <= 0 or truelat1 > 90:
|
super().__init__(code=code, lat1=lat1, lon1=lon1, dx=dx, dy=dy, nx=nx, ny=ny,
|
||||||
pass
|
stdlon=stdlon, truelat1=truelat1, knowni=knowni, knownj=knownj, re_m=re_m) # 初始化各变量
|
||||||
dummy_lon1 = (lon1 + 180) % 360 - 180 # 限制经度范围
|
self.set_merc() # 计算其他变量
|
||||||
dummy_stdlon = (stdlon + 180) % 360 - 180 # 限制中央经线范围
|
|
||||||
if knowni is None and knownj is None:
|
|
||||||
knowni = (nx + 1) / 2
|
|
||||||
knownj = (ny + 1) / 2
|
|
||||||
if dy is None: # 设置dy, 如果dy不存在, 则利用dx给定
|
|
||||||
dy = dx
|
|
||||||
if truelat1 < 0: # 所在半球, 1为北半球, -1为南半球
|
|
||||||
hemi = -1
|
|
||||||
else:
|
|
||||||
hemi = 1
|
|
||||||
if abs(truelat2) > 90: # 如果标准纬线2超过范围, 则用标准纬线1赋值
|
|
||||||
truelat2 = truelat1
|
|
||||||
super().__init__(code=code, lat1=lat1, lon1=dummy_lon1, dx=dx, dy=dy,
|
|
||||||
stdlon=dummy_stdlon, truelat1=truelat1, truelat2=truelat2, hemi=hemi,
|
|
||||||
knowni=knowni, knownj=knownj, re_m=re_m) # 初始化各变量
|
|
||||||
self.rebydx = re_m / dx # 地球半径除以网格距
|
|
||||||
self.set_lc() # 计算其他变量
|
|
||||||
self.check_init() # 确认是否所有变量都计算完毕
|
|
||||||
def set_merc(self):
|
def set_merc(self):
|
||||||
clain = np.cos(np.deg2rad(self.truelat1))
|
clain = np.cos(np.deg2rad(self.truelat1)) # 标准纬线在赤道面投影到地心的距离/地球半径
|
||||||
self.dlon = self.dx / (self.re_m * clain)
|
self.dlon = self.dx / (self.re_m * clain) # 标准纬线附近,单位网格的经度变化
|
||||||
# 计算原点到迟到的距离,并保存在 self.rsw 变量中
|
# 计算原点到赤道的距离,并保存在 self.rsw 变量中
|
||||||
self.rsw = 0 if self.lat1 == 0 else np.log(np.tan(0.5*(np.deg2rad(self.lat1+90))))/self.dlon
|
self.rsw = 0 if self.lat1 == 0 else np.log(np.tan(0.5*(np.deg2rad(self.lat1+90))))/self.dlon
|
||||||
def llij_merc(self, lat, lon):
|
|
||||||
|
def llij(self, lon, lat):
|
||||||
deltalon = lon - self.lon1
|
deltalon = lon - self.lon1
|
||||||
deltalon = (deltalon + 180) % 360 - 180
|
deltalon = (deltalon + 180) % 360 - 180
|
||||||
i = self.knowni + deltalon / np.rad2deg(self.dlon)
|
i = self.knowni + deltalon / np.rad2deg(self.dlon)
|
||||||
j = self.knownj + np.log(np.tan(0.5*np.deg2rad(lat + 90)))/self.dlon - self.rsw
|
j = self.knownj + np.log(np.tan(0.5*np.deg2rad(lat + 90)))/self.dlon - self.rsw
|
||||||
return i, j
|
return i, j
|
||||||
def ijll_merc(self, i, j):
|
|
||||||
lat = np.rad2deg(2*np.arctan(np.exp(self.dlon*(self.rsw + j - self.knownj)))) - 90
|
def ijll(self, i, j):
|
||||||
|
lat = 2*np.rad2deg(np.arctan(np.exp(self.dlon*(self.rsw + j - self.knownj)))) - 90
|
||||||
lon = (i - self.knowni) * np.rad2deg(self.dlon) + self.lon1
|
lon = (i - self.knowni) * np.rad2deg(self.dlon) + self.lon1
|
||||||
lon = (lon + 180) % 360 - 180
|
lon = (lon + 180) % 360 - 180
|
||||||
return lon, lat
|
return lon, lat
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
proj = proj_LC(truelat1=45, truelat2=15, lat1=30, lon1=108, stdlon=108, dx=3000, dy=3000, nx=2025, ny=2025)
|
"""
|
||||||
proj.llij_lc(108, 30)
|
主要用于测试投影的基本功能,坐标前后转换是否正常,具体测试项如下:
|
||||||
proj.ijll_lc(1013, 1013)
|
1. 构建投影
|
||||||
x = np.arange(1, 2000, 10)
|
2. 坐标点位转换以及转换结果是否一致
|
||||||
y = np.arange(1, 2000, 10)
|
3. 坐标序列转换以及转换结果是否一致
|
||||||
print(proj.ijlls_lc(x, y))
|
"""
|
||||||
print(proj.ijll_lc(x[0], y[0]), proj.ijll_lc(x[-1], y[-1]))
|
logging.info("test proj_LC")
|
||||||
x, y = proj.llij_lc(0, 90)
|
try:
|
||||||
lon, lat = proj.ijll_lc(x, y)
|
proj = proj_LC(truelat1=45, truelat2=15, lat1=30, lon1=108, stdlon=108, dx=3000, dy=3000, nx=2025, ny=2025)
|
||||||
x, y = proj.llij_lc(lon, lat)
|
x0, y0 = 0, 0
|
||||||
lon1, lat1 = proj.ijll_lc(x, y)
|
lon, lat = proj.grid_lonlat(x0, y0)
|
||||||
print(lon, lat, x, y, lon1, lat1)
|
x1, y1 = proj.grid_id_float(lon, lat)
|
||||||
|
if not ((x0 - x1)**2 + (y0 - y1)**2)**0.5 < 1e-5:
|
||||||
|
logging.error((x0, y0), (x1, y1))
|
||||||
|
raise ValueError("point convert error!")
|
||||||
|
x0_array, y0_array = np.arange(1, 2000, 10), np.arange(1, 2000, 10)
|
||||||
|
lon_array, lat_array = proj.grid_lonlat(x0_array, y0_array)
|
||||||
|
x1_array, y1_array = proj.grid_id_float(lon_array, lat_array)
|
||||||
|
if not np.max(((x0_array - x1_array)**2 + (y0_array - y1_array)**2)**0.5) < 1e-5:
|
||||||
|
raise ValueError("array convert error!")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("proj_LC not pass!")
|
||||||
|
logging.error(e)
|
||||||
|
else:
|
||||||
|
logging.info("proj_LC pass.")
|
||||||
|
|
||||||
|
logging.info("test proj_MERC")
|
||||||
|
try:
|
||||||
|
proj = proj_MERC(truelat1=30, lat1=30, lon1=108, stdlon=108, dx=3000, dy=3000, nx=2025, ny=2025)
|
||||||
|
x0, y0 = 0, 0
|
||||||
|
lon, lat = proj.grid_lonlat(x0, y0)
|
||||||
|
x1, y1 = proj.grid_id_float(lon, lat)
|
||||||
|
if not ((x0 - x1)**2 + (y0 - y1)**2)**0.5 < 1e-5:
|
||||||
|
logging.error(f"{(x0, y0)}, {(x1, y1)}")
|
||||||
|
raise ValueError("point convert error!")
|
||||||
|
x0_array, y0_array = np.arange(1, 2000, 10), np.arange(1, 2000, 10)
|
||||||
|
lon_array, lat_array = proj.grid_lonlat(x0_array, y0_array)
|
||||||
|
x1_array, y1_array = proj.grid_id_float(lon_array, lat_array)
|
||||||
|
if not np.max(((x0_array - x1_array)**2 + (y0_array - y1_array)**2)**0.5) < 1e-5:
|
||||||
|
raise ValueError("array convert error!")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("proj_MERC not pass!")
|
||||||
|
logging.error(e)
|
||||||
|
else:
|
||||||
|
logging.info("proj_MERC pass.")
|
Reference in New Issue
Block a user