简体   繁体   English

计算 Basemap 创建的等高线内的非投影区域

[英]Calculate the non-projected area inside a contour line created by Basemap

I am currently trying to determine the area inside specfic contour lines on a Mollweide map projection using Basemap.我目前正在尝试使用 Basemap 确定 Mollweide 地图投影上特定等高线内的区域。 Specifically, what I'm looking for is the area of various credible intervals in square degrees (or degrees 2 ) of an astronomical event on the celestial sphere.具体来说,我正在寻找的是天球上天文事件的各种可信区间的面积(以平方度(或度2 )为单位)。 The plot is shown below:该图如下所示:

在此处输入图像描述

Fortunately, a similar question has already been answered before that helps considerably.幸运的是,之前已经回答了一个类似的问题,这很有帮助。 The method outlined in the answer is able to account for holes within the contour as well which is a necessity for my use case.答案中概述的方法也能够解释轮廓内的孔,这对我的用例来说是必需的。 My adapted code for this particular method is provided below:下面提供了我为这种特定方法改编的代码:

# generate a regular lat/lon grid.
nlats = 300; nlons = 300; delta_lon = 2.*np.pi/(nlons-1); delta_lat = np.pi/(nlats-1)
lats = (0.5*np.pi-delta_lat*np.indices((nlats,nlons))[0,:,:])
lons = (delta_lon*np.indices((nlats,nlons))[1,:,:] - np.pi)

map = Basemap(projection='moll',lon_0=0, celestial=True)

# compute native map projection coordinates of lat/lon grid
x, y = map(lons*180./np.pi, lats*180./np.pi)    

areas = []
cred_ints = [0.5,0.9]

for k in range(len(cred_ints)):

    cs = map.contourf(x,y,p1,levels=[0.0,cred_ints[k]]) ## p1 is the cumulative distribution across all points in the sky (usually determined via KDE on the data)
    
    ##organizing paths and computing individual areas
    paths = cs.collections[0].get_paths()
    #help(paths[0])
    area_of_individual_polygons = []
    for p in paths:
        sign = 1  ##<-- assures that area of first(outer) polygon will be summed
        verts = p.vertices
        codes = p.codes
        idx = np.where(codes==Path.MOVETO)[0]
        vert_segs = np.split(verts,idx)[1:]
        code_segs = np.split(codes,idx)[1:]
        for code, vert in zip(code_segs,vert_segs):

            ##computing the area of the polygon
            area_of_individual_polygons.append(sign*Polygon(vert[:-1]).area)
            sign = -1 ##<-- assures that the other (inner) polygons will be subtracted

    ##computing total area        
    total_area = np.sum(area_of_individual_polygons)
    
    print(total_area)
    
    areas.append(total_area)

print(areas)

As far as I can tell this method works beautifully... except for one small wrinkle: this calculates the area using the projected coordinate units.据我所知,这种方法效果很好......除了一个小皱纹:它使用投影坐标单位计算面积。 I'm not entirely sure what the units are in this case but they are definitely not degrees 2 (the calculated areas are on the order of 10 13 units 2 ... maybe the units are pixels?).我不完全确定在这种情况下的单位是什么,但它们绝对不是2度(计算的面积大约是 10 13 个单位2 ......也许单位是像素?)。 As alluded to earlier, what I'm looking for is how to calculate the equivalent area in the global coordinate units, ie in degrees 2 .如前所述,我正在寻找的是如何以全局坐标单位计算等效面积,即2度。

Is there a way to convert the area calculated in the projected domain back into the global domain in square degrees?有没有办法将在投影域中计算的面积转换回以平方为单位的全局域? Or perhaps is there a way to modify this method so that it determines the area in degrees 2 from the get go?或者也许有办法修改这个方法,以便它从一开始就确定2度的区域?

Any help will be greatly appreciated!任何帮助将不胜感激!

For anyone that comes across this question, while I didn't figure out a way to directly convert the projected area back into the global domain, I did develop a new solution by modifying the original approach and utilizing Green's theorem with the contour path information.对于遇到这个问题的任何人,虽然我没有找到将投影区域直接转换回全局域的方法,但我确实通过修改原始方法并利用格林定理和轮廓路径信息开发了一个新的解决方案。 First, some background information (the code is provided at the end of the answer for anyone not interested):首先,一些背景信息(代码在答案末尾提供给任何不感兴趣的人):

The solid angle encompassed by a region R on a sphere (credible intervals on the celestial sphere in this context) is given by the following:球体上区域 R 所包围的立体角(在此上下文中为天球上的可信区间)由下式给出:

在此处输入图像描述

Using Green's theorem, ie使用格林定理,即

在此处输入图像描述

we can convert our surface integral into a contour integral over the region's boundary C. We just need to solve for the functions Q and P which is relatively straightforward,我们可以将表面积分转换为区域边界 C 上的轮廓积分。我们只需要求解相对简单的函数 Q 和 P,

在此处输入图像描述

在此处输入图像描述

Without loss of generality, the simplest solution to this equation is不失一般性,这个方程最简单的解是

在此处输入图像描述

Plugging these functions into Green's theorem and equating it with the total solid angle gives us our final equation:将这些函数代入格林定理并将其与总立体角相等,就得到了我们的最终方程:

在此处输入图像描述

Using the contour path information from matplotlib.contourf, and the sign specifying if a path is a hole or not, we can now easily determine the total solid angle any particular contour C encompasses.使用来自 matplotlib.contourf 的轮廓路径信息,以及指定路径是否为孔的符号,我们现在可以轻松确定任何特定轮廓 C 包含的总立体角。 The only complication is accounting for negative areas (in this case contours that traverse the lower hemisphere).唯一的并发症是考虑负区域(在这种情况下,轮廓穿过下半球)。 However, this is easily resolved by splitting up the path information for a contour into separate paths for each hemisphere and negating the areas obtained for the paths in the lower hemisphere.然而,这很容易解决,方法是将轮廓的路径信息拆分为每个半球的单独路径,并否定下半球路径获得的区域。

The code that actually carries out the calculation is provided below:下面提供了实际执行计算的代码:

# generate a regular lat/lon grid.
nlats = 300; nlons = 300; delta_lon = 2.*np.pi/(nlons-1); delta_lat = np.pi/(nlats-1)
lats = (0.5*np.pi-delta_lat*np.indices((nlats,nlons))[0,:,:])
lons = (delta_lon*np.indices((nlats,nlons))[1,:,:])


### FOLLOWING CODE DETERMINES CREDIBLE INTERVAL SKY AREA IN DEG^2  ###

# collect and organize contour data for each credible interval

cred_ints = [0.5,0.9]    
all_lines = []

for k in range(len(cred_ints)):       

    cs = plt.contourf(lons,lats,p1,levels=[0.0,cred_ints[k]])  ## p1 is the cumulative distribution across all points in the sky (usually determined via KDE of the dataset in question)       

    paths = cs.collections[0].get_paths()

    lines = []

    for p in paths:            

        sign = 1  ##<-- assures that area of first(outer) paths will be summed

        verts = p.vertices
        codes = p.codes
        idx = np.where(codes==Path.MOVETO)[0] 

        vert_segs = np.split(verts,idx)[1:]
        code_segs = np.split(codes,idx)[1:]            

        temp = []

        for code, vert in zip(code_segs,vert_segs):                

            temp.append((sign,vert))                
            sign = -1 ##<-- assures that the other (inner) paths/holes will be subtracted

        lines.append(temp)

    all_lines.append(lines) 


# use contour data to calculate relevant areas

ci_areas = []

for k in range(len(cred_ints)):  ### number of credible intervals 

    ci_contours = all_lines[k]

    contour_areas = []

    for j in range(len(ci_contours)):  ### number of different contours for current credible interval       

        contour_paths = ci_contours[j]

        path_areas = [] 

        for i in range(len(contour_paths)):  ### number of unique paths that make up current contour (includes holes)

            areas = []

            sign = contour_paths[i][0]
            path = contour_paths[i][1]

            pos_idx = np.where(path[:,1]>=0)[0] ## upper hemisphere paths
            pos_path = np.vstack(np.split(path[pos_idx],np.where(np.diff(pos_idx)!=1)[0]+1))

            neg_idx = np.where(path[:,1]<0)[0]  ## lower hemisphere paths
            temp_neg_path = np.vstack(np.split(path[neg_idx],np.where(np.diff(neg_idx)!=1)[0]+1))                        
            neg_path = np.roll(temp_neg_path, (np.shape(temp_neg_path)[0]//4)*2)  ##cycle array so the cutoff occurs near the middle and not at the beginning/end (this leads to spurious values for some reason)


            ## the np.abs step in the following lines should be redundant but I've kept it in for peace of mind :)   

            posA = sign*np.abs(np.sum(-np.cos(pos_path[:,1])[:-1]*np.diff(pos_path[:,0])))

            areas.append(posA)

            negA = sign*np.abs(np.sum(np.cos(neg_path[:,1])[:-1]*np.diff(neg_path[:,0])))

            areas.append(negA)

            path_areas.append(np.sum(areas))

        single_contour_area = np.sum(path_areas)

        contour_areas.append(single_contour_area)

    total_area = np.sum(contour_areas)

    ci_areas.append(total_area*(180/np.pi)**2)

print(ci_areas)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM