如何使用地理/坐标而不是像素坐标沿曲线提取一维轮廓?

How to extract a One dimensional profile along a curve using Geographic/Co-ordinates instead of pixel Coords?

提问人:LordHammer 提问时间:3/17/2023 最后编辑:LordHammer 更新时间:3/28/2023 访问量:78

问:

enter image description here如何从给定地理坐标而不是像素坐标的 Array 中获取 1D 轮廓。 我在地理坐标(NASA GOLD Mission)中有辐射度数据。 每个半球的数据都存储在单独的文件中。我想沿一条曲线(北纬 10 度)拍摄拼接半球的 1D 剖面图,该曲线以 Geo 坐标表示,

我试图使用map_coordinates来调整这里的答案,但我意识到它是在像素坐标中,这就是为什么我认为配置文件与地图不对应的原因; 为了获得一维剖面图,我将地图坐标的输出与输入曲线 (X) 值作图,以便获得经度的变化。 另一个黄色的剖面图也与地图中的数据不对应; 当我使用 imshow 绘制数据时,它看起来与地图上不同;imshow of data

数据和 Python 代码在这里 数据+代码;

GOLD map and 1D profile along 10 deg North dip

from netCDF4 import Dataset  
import glob, matplotlib,os;matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy.ma as ma

Mag = pd.read_csv(r"\mag_cords.csv")
X_dip10s,Y_dip10s = Mag.LON10S.to_numpy(),Mag.LAT10S.to_numpy()
g = Dataset('GOLD_L1C_CH*23*.nc','r'); 
wavelength = g.variables['WAVELENGTH'][:]
radiance = g.variables['RADIANCE'][:]
lat = g.variables['REFERENCE_POINT_LAT'][:] 
lon = g.variables['REFERENCE_POINT_LON'][:] 
g.close()
O5s_ids = np.argwhere((134.3 <= wavelength[50,25,:]) & (wavelength[50,25,:] <= 137.7))
O5s = np.array(np.nansum(radiance[:,:,O5s_ids],axis = 2))*.04 # integrate under the peak!
O5s = np.transpose(O5s[:,:,0])
x=lon[9:-8,9:-8];y=lat[9:-8,9:-8];z=O5s[9:-8,9:-8].T;z=abs(z)#To remove Nans
z2=np.ma.masked_array(z, (z > 30)) 

Zs=scipy.ndimage.map_coordinates(np.transpose(z),
np.vstack((X_dip10s,Y_dip10s)),
mode="nearest")
Xs=scipy.ndimage.map_coordinates(np.transpose(x),
np.vstack((X_dip10s,Y_dip10s)),mode="nearest") 
                                                                                                                         
fig=plt.figure(figsize=(10,10))
axarr = fig.add_subplot(211,projection=ccrs.PlateCarree())
ax2 = fig.add_subplot(212)
grid_lines =axarr.gridlines(draw_labels=True'); axarr.coastlines()
cs1 = axarr.contourf(x,y, z2, transform=ccrs.PlateCarree(),cmap='jet',levels=50)
ax2.plot(X_dip10s,Zs, 'k-', Xs ,Zs, 'k-')


  [1]: https://i.stack.imgur.com/66C4P.png
索引 地图 切片 scikit-image cartopy

评论

0赞 LordHammer 3/17/2023
我还希望地图与 1D 剖面对齐,以便 x 轴相同。
0赞 LordHammer 3/25/2023
澄清一下,数据来自地球静止卫星,该卫星测量各种波长的辐射。这些数据以地球上的纬度和纬度为基准。我想使用 IGRF 倾角纬度提取剖面,例如沿磁赤道(与地理赤道不同)倾角 Lat=0 度,倾角纬度 =10、15 等

答:

1赞 raphael 3/20/2023 #1

有趣的问题!

我是 EOmaps 的开发人员,EOmaps 是一个旨在简化创建这种自定义地理数据分析小部件的库,所以我很感兴趣:-)

我快速浏览了一下您的数据...这是一个非常奇怪的 netcdf,我不知道是什么定义了您用来识别点的行,所以我只是重用您的代码来提取数据。

这是一个快速解决方案,它使用 KDTree 来识别相对于线点的最近点......它不是 100% 精确,因为它根据以度为单位定义的最大距离而不是地球上的实际距离来识别这些点,但它工作得很好,我认为如果你需要更精确的东西,你应该能够从这里拿走它......

同样重要的是要注意,此方法将向您显示实际数据,而不是由用于显示数据的算法创建的一些插值点!(所以这条线不会很好看,它只会显示最接近这条线的实际数据值)

enter image description here

from pathlib import Path
from itertools import chain

from scipy.spatial import KDTree
import xarray as xar
import pandas as pd
import numpy as np

from eomaps import Maps


# --------------------------------------------- read the data
p = Path(r"GOLD_data-20230319T161539Z-001\GOLD_data")

with xar.open_dataset(p / "GOLD_L1C_CHA_NI1_2018_286_23_10_v04_r01_c01.nc") as ncfile:
    lon, lat = ncfile.REFERENCE_POINT_LON.values, ncfile.REFERENCE_POINT_LAT.values
    wavelength = ncfile.WAVELENGTH.values
    radiance = ncfile.RADIANCE.values

O5s_ids = np.argwhere((134.3 <= wavelength[50,25,:]) & (wavelength[50,25,:] <= 137.7))
O5s = np.array(np.nansum(radiance[:,:,O5s_ids],axis = 2))*.04 # integrate under the peak!
O5s = np.transpose(O5s[:,:,0])
x=lon[9:-8,9:-8]
y=lat[9:-8,9:-8]
z=O5s[9:-8,9:-8].T


Mag = pd.read_csv(p / "mag_cords.csv")
X_dip10s,Y_dip10s = Mag.LON10S.to_numpy(),Mag.LAT10S.to_numpy()
line = np.column_stack((X_dip10s, Y_dip10s))


# --------------------------------------------- create a new map
m = Maps(ax=211, crs=4326)
m.set_extent((-61.8,-20.5,-1.5,24.5))
m.add_feature.preset.coastline()
m.add_feature.preset.ocean()

# plot the data
m.set_data(data=z, x=x, y=y, crs=4326)
m.set_shape.ellipses(radius=0.35)
m.set_classify.UserDefined(bins=np.linspace(0, 20, 11))
m.plot_map(cmap="viridis", vmin=0, vmax=28)
m.add_colorbar(orientation="vertical")

# add a normal matplotlib axis (and share x-limits)
ax = m.f.add_subplot(212)
ax.sharex(m.ax)
# turn off navigation on the normal matplotlib plot
ax.set_navigate(False)

# position axes 
m.apply_layout(
    {'figsize': [6.95, 4.8],
     '0_map': [0.1, 0.44678, 0.5875, 0.53552],
     '1_cb': [0.75, 0.04546, 0.2125, 0.95484],
     '1_cb_histogram_size': 0.8,
     '2_': [0.1, 0.10859, 0.5875, 0.32578]}
    )

# setup a new map-layer (will be used to draw identified points)
m2 = m.new_layer()

# --------------------------------------------- identify points along line
tree = KDTree(np.column_stack((x.ravel(), y.ravel())))

def get_lineoffset(pos, **kwargs):
    # get the offset required to shift the line to the click position
    x, y = line.T
    minxid = np.argmin(abs(pos[0] - x))
    offset = pos[1] - y[minxid]
    return offset

def identify_points(pts, max_dist):
    # identify closest points with respect to the line
    tree2 = KDTree(pts)
    q = tree2.query_ball_tree(tree, max_dist)
    q = list(chain(*[i for i in q if len(i) > 0]))
    coords = tree.data[q].T
    data = z.ravel()[q]
    return coords, data

def indicate_found_points(pts, offset=0, max_dist=1):
    # offset line
    pts = pts + [0, offset]
    # identify closest points
    coords, data = identify_points(pts, max_dist=max_dist)
    
    # plot identified points on the map (replace on each click)
    m2.set_data(data, coords[0], coords[1])
    m2.set_shape.ellipses(radius=0.2)
    m2.plot_map(set_extent=False, zorder=2, fc="r")
    
    # sort found points so we can plot a line
    sortp = np.argsort(coords[0])
    lx, ly = coords[0][sortp], data[sortp]    
    
    # plot a line on the map
    l, = m.ax.plot(*pts.T, c="r", lw=0.5)
    # remove it on next pick
    m.cb.pick.add_temporary_artist(l)
    
    # plot data on normal matpltoib plot
    l2, = ax.plot(lx, ly, lw=0.25, c="k", zorder=0)
    # color points same as in the map
    sc = ax.scatter(lx, ly, c=ly, s=10, norm=m._norm, cmap=m._cbcmap)
    # remove it on next pick
    m.cb.pick.add_temporary_artist(l2)
    m.cb.pick.add_temporary_artist(sc)

    ax.relim()
    ax.autoscale_view()
    m.redraw()

def callback(pos, max_dist=1, **kwargs):
    offset = get_lineoffset(pos)
    indicate_found_points(line, offset=offset, max_dist=max_dist)

# attach the callback to the map
m.cb.pick.attach(callback, max_dist=0.75)

评论

0赞 LordHammer 3/26/2023
这看起来是一个很好的解决方案。我使用 minicondas Dbn 安装,但是当我运行此命令“from eomaps import Maps”错误时:“ ImportError:无法从'matplotlib.tri'导入名称'TriMesh'(C:\Users\simba\miniconda3\envs\eomaps\Lib\site-packages\matplotlib\tri_init_.py)”
0赞 raphael 3/27/2023
嘿,这是因为您正在使用...更新应该可以解决问题!(github.com/matplotlib/matplotlib/issues/24105)(我必须为此实现一个向后移植,以便旧版本也可以工作......matplotlib v3.5.xmatplotlib v3.6.x
0赞 LordHammer 3/27/2023
stackoverflow.com/users/9703451/raphael,我实际上在我的 miniconda 上安装了 matplotlib 3.7.1。我应该降级到 3.6.0 吗?
0赞 raphael 3/27/2023
嘿嘿,这确实出乎意料......您是否可以使用旧的 EOmaps 版本?(因为不再从...我刚刚测试过,我没有遇到任何问题......我在这里打开了一个关于 v3.5.x 的问题......如果您提供有关设置的一些详细信息,我可以尝试帮助查明您的问题。eomaps v4.4.3TriMeshmatplotlib.trieomaps v6.3matplotlib v3.7.1
0赞 LordHammer 3/28/2023
嘿,我在哪里可以得到这些详细信息?