使用python生成RA与DEC赤道坐标图

时间:2015-04-08 21:06:18

标签: python matplotlib astronomy polar-coordinates

我正在尝试生成一个看起来或多或少像这样的赤道坐标图:

enter image description here

(该图来自this article,它显示赤道坐标中Large and Small MCs的位置)

关于这个情节需要注意的重要事项:

  • theta轴(即:右上角)位于 h:m:s (小时,分钟,秒),因为它习惯于天文学,而不是度数默认polar选项位于matplotlib
  • r轴(即:赤纬)从-90º向外增加,网格以(0h,-90º)为中心。
  • 该图是剪裁,这意味着只有一部分显示而不是整个圆圈(默认为matplotlib)。

使用polar=True中的matplotlib选项,我设法生成的最接近的地块是(MWE以下,数据文件here;某些点不存在与上面的图像相比,因为数据文件有点小):

enter image description here

我还需要在绘图中添加第三列数据,这就是为什么我相应地为z数组添加颜色条和颜色的原因:

enter image description here

所以我现在最需要的是一种剪辑情节的方法。主要基于this questionthis example @cphlewis与his answer非常接近,但有些事情仍然缺失(在他的回答中提到)。

非常感谢任何有关此问题的帮助和/或指示。


MWE

(注意我使用gridspec来定位子图,因为我需要在同一输出图像文件中生成其中的几个)

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

def skip_comments(f):
    '''
    Read lines that DO NOT start with a # symbol.
    '''
    for line in f:
        if not line.strip().startswith('#'):
            yield line

def get_data_bb():
    '''RA, DEC data file.
    '''

    # Path to data file.
    out_file = 'bb_cat.dat'

    # Read data file
    with open(out_file) as f:
        ra, dec = [], []

        for line in skip_comments(f):
            ra.append(float(line.split()[0]))
            dec.append(float(line.split()[1]))

    return ra, dec

# Read RA, DEC data from file.
ra, dec = get_data_bb()
# Convert RA from decimal degrees to radians.
ra = [x / 180.0 * 3.141593 for x in ra]

# Make plot.
fig = plt.figure(figsize=(20, 20))
gs = gridspec.GridSpec(4, 2)
# Position plot in figure using gridspec.
ax = plt.subplot(gs[0], polar=True)
ax.set_ylim(-90, -55)

# Set x,y ticks
angs = np.array([330., 345., 0., 15., 30., 45., 60., 75., 90., 105., 120.])
plt.xticks(angs * np.pi / 180., fontsize=8)
plt.yticks(np.arange(-80, -59, 10), fontsize=8)
ax.set_rlabel_position(120)
ax.set_xticklabels(['$22^h$', '$23^h$', '$0^h$', '$1^h$', '$2^h$', '$3^h$',
    '$4^h$', '$5^h$', '$6^h$', '$7^h$', '$8^h$'], fontsize=10)
ax.set_yticklabels(['$-80^{\circ}$', '$-70^{\circ}$', '$-60^{\circ}$'],
    fontsize=10)

# Plot points.
ax.scatter(ra, dec, marker='o', c='k', s=1, lw=0.)

# Use this block to generate colored points with a colorbar.
#cm = plt.cm.get_cmap('RdYlBu_r')
#z = np.random.random((len(ra), 1))  # RGB values
#SC = ax.scatter(ra, dec, marker='o', c=z, s=10, lw=0., cmap=cm)
# Colorbar
#cbar = plt.colorbar(SC, shrink=1., pad=0.05)
#cbar.ax.tick_params(labelsize=8)
#cbar.set_label('colorbar', fontsize=8)

# Output png file.
fig.tight_layout()
plt.savefig(ra_dec_plot.png', dpi=300)

3 个答案:

答案 0 :(得分:6)

咀嚼AxisArtist的例子实际上非常有前景(这结合了两个AxisArtist的例子 - 如果AxisArtist用RA情节编写,我不会感到惊讶):

enter image description here

还在做:

  1. 赤字应该从原点的-90到0
  2. 能够使用 并添加一个颜色栏
  3. 如果在他们之外绘图,则调整限制
  4. 审美:

    1. 轴标签中的衬线字体
    2. 提升的虚线网格
    3. 其他什么?

      """
      An experimental support for curvilinear grid.
      """
      import numpy as np
      import  mpl_toolkits.axisartist.angle_helper as angle_helper
      import matplotlib.cm as cmap
      from matplotlib.projections import PolarAxes
      from matplotlib.transforms import Affine2D
      
      from mpl_toolkits.axisartist import SubplotHost
      
      from mpl_toolkits.axisartist import GridHelperCurveLinear
      
      
      def curvelinear_test2(fig):
          """
          polar projection, but in a rectangular box.
          """
          global ax1
      
          # see demo_curvelinear_grid.py for details
          tr = Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform()
      
          extreme_finder = angle_helper.ExtremeFinderCycle(10, 60,
                                                           lon_cycle = 360,
                                                           lat_cycle = None,
                                                           lon_minmax = None,
                                                           lat_minmax = (0, np.inf),
                                                           )
      
          grid_locator1 = angle_helper.LocatorHMS(12) #changes theta gridline count
          tick_formatter1 = angle_helper.FormatterHMS()
      
          grid_locator2 = angle_helper.LocatorDMS(6)
          tick_formatter2 = angle_helper.FormatterDMS()
      
          grid_helper = GridHelperCurveLinear(tr,
                                              extreme_finder=extreme_finder,
                                              grid_locator1=grid_locator1,
                                              tick_formatter1=tick_formatter1,
                                              grid_locator2=grid_locator2,
                                              tick_formatter2=tick_formatter2
                                              )
      
      
          ax1 = SubplotHost(fig, 1, 1, 1, grid_helper=grid_helper)
      
          # make ticklabels of right and top axis visible.
          ax1.axis["right"].major_ticklabels.set_visible(True)
          ax1.axis["top"].major_ticklabels.set_visible(True)
          ax1.axis["bottom"].major_ticklabels.set_visible(True) #Turn off? 
          # let right and bottom axis show ticklabels for 1st coordinate (angle)
          ax1.axis["right"].get_helper().nth_coord_ticks=0
          ax1.axis["bottom"].get_helper().nth_coord_ticks=0
      
      
      
          fig.add_subplot(ax1)
      
          grid_helper = ax1.get_grid_helper()
      
          ax1.set_aspect(1.)
          ax1.set_xlim(-4,15) # moves the origin left-right in ax1
          ax1.set_ylim(-3, 20) # moves the origin up-down
      
          ax1.set_ylabel('90$^\circ$ + Declination')
          ax1.set_xlabel('Ascension')
          ax1.grid(True)
          #ax1.grid(linestyle='--', which='x') # either keyword applies to both
          #ax1.grid(linestyle=':', which='y')  # sets of gridlines
      
          return tr
      
      import matplotlib.pyplot as plt
      fig = plt.figure(1, figsize=(5, 5))
      fig.clf()
      
      tr = curvelinear_test2(fig) # tr.transform_point((x, 0)) is always (0,0)
                                  # => (theta, r) in but (r, theta) out...
      r_test =   [0, 1.2, 2.8, 3.8, 5,  8,  10, 13.3, 17]  # distance from origin
      deg_test = [0,  -7, 12,  28,  45, 70, 79, 90,   100] # degrees ascension
      out_test = tr.transform(zip(deg_test, r_test))
      
      sizes = [40, 30, 10, 30, 80, 33, 12, 48, 45]
      #hues = [.9, .3, .2, .8, .6, .1, .4, .5,.7] # Oddly, floats-to-colormap worked for a while.
      hues = np.random.random((9,3)) #RGB values
      
      ax1.scatter(out_test[:,0], #ax1 is a global
                  out_test[:,1],
                  s=sizes,
                  c=hues,
                  #cmap=cmap.RdYlBu_r,
                  zorder=9) #on top of gridlines
      
      
      plt.show()
      

答案 1 :(得分:5)

获取颜色栏可以通过将OP代码与@cphlewis's excellent answer合并来完成。我已将此作为request of the OP in chat的交钥匙解决方案发布。第一个版本的代码只添加一个颜色条,最终版本(在编辑2下)执行轴仿射转换并纠正一些参数/简化代码以完全符合OP规范。

"""
An experimental support for curvilinear grid.
"""
import numpy as np
import  mpl_toolkits.axisartist.angle_helper as angle_helper
import matplotlib.cm as cmap
from matplotlib.projections import PolarAxes
from matplotlib.transforms import Affine2D

from mpl_toolkits.axisartist import SubplotHost

from mpl_toolkits.axisartist import GridHelperCurveLinear


def curvelinear_test2(fig):
    """
    polar projection, but in a rectangular box.
    """
    global ax1

    # see demo_curvelinear_grid.py for details
    tr = Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform()

    extreme_finder = angle_helper.ExtremeFinderCycle(10, 60,
                                                     lon_cycle = 360,
                                                     lat_cycle = None,
                                                     lon_minmax = None,
                                                     lat_minmax = (0, np.inf),
                                                     )

    grid_locator1 = angle_helper.LocatorHMS(12) #changes theta gridline count
    tick_formatter1 = angle_helper.FormatterHMS()

    grid_locator2 = angle_helper.LocatorDMS(6)
    tick_formatter2 = angle_helper.FormatterDMS()

    grid_helper = GridHelperCurveLinear(tr,
                                        extreme_finder=extreme_finder,
                                        grid_locator1=grid_locator1,
                                        tick_formatter1=tick_formatter1,
                                        grid_locator2=grid_locator2,
                                        tick_formatter2=tick_formatter2
                                        )


    ax1 = SubplotHost(fig, 1, 1, 1, grid_helper=grid_helper)

    # make ticklabels of right and top axis visible.
    ax1.axis["right"].major_ticklabels.set_visible(True)
    ax1.axis["top"].major_ticklabels.set_visible(True)
    ax1.axis["bottom"].major_ticklabels.set_visible(True) #Turn off? 
    # let right and bottom axis show ticklabels for 1st coordinate (angle)
    ax1.axis["right"].get_helper().nth_coord_ticks=0
    ax1.axis["bottom"].get_helper().nth_coord_ticks=0



    fig.add_subplot(ax1)

    grid_helper = ax1.get_grid_helper()

    ax1.set_aspect(1.)
    ax1.set_xlim(-4,15) # moves the origin left-right in ax1
    ax1.set_ylim(-3, 20) # moves the origin up-down

    ax1.set_ylabel('90$^\circ$ + Declination')
    ax1.set_xlabel('Ascension')
    ax1.grid(True)
    #ax1.grid(linestyle='--', which='x') # either keyword applies to both
    #ax1.grid(linestyle=':', which='y')  # sets of gridlines

    return tr

import matplotlib.pyplot as plt
fig = plt.figure(1, figsize=(5, 5))
fig.clf()

tr = curvelinear_test2(fig) # tr.transform_point((x, 0)) is always (0,0)
                            # => (theta, r) in but (r, theta) out...
r_test =   [0, 1.2, 2.8, 3.8, 5,  8,  10, 13.3, 17]  # distance from origin
deg_test = [0,  -7, 12,  28,  45, 70, 79, 90,   100] # degrees ascension
out_test = tr.transform(zip(deg_test, r_test))

sizes = [40, 30, 10, 30, 80, 33, 12, 48, 45]
#hues = [.9, .3, .2, .8, .6, .1, .4, .5,.7] # Oddly, floats-to-colormap worked for a while.
hues = np.random.random((9,3)) #RGB values

# Use this block to generate colored points with a colorbar.
cm = plt.cm.get_cmap('RdYlBu_r')
z = np.random.random((len(r_test), 1))  # RGB values

SC = ax1.scatter(out_test[:,0], #ax1 is a global
            out_test[:,1],
            s=sizes,
            c=z,
            cmap=cm,
            zorder=9) #on top of gridlines

# Colorbar
cbar = plt.colorbar(SC, shrink=1., pad=0.05)
cbar.ax.tick_params(labelsize=8)
cbar.set_label('colorbar', fontsize=8)


plt.show()

<强> 修改

整理参数,添加OP数据,删除冗余会产生以下图表。仍然需要将数据集中在-90而不是0 - 目前这是被黑客攻击的,但我确信curvelinear_test2()可以更改为帐户...

PIcture matching OP desired format

编辑2

在这个答案中关于中间版本的OP评论之后,下面的最终版本给出了帖子最后的情节 - 在dec轴上的-90和子图演示

"""
An experimental support for curvilinear grid.
"""
import numpy as np
import  mpl_toolkits.axisartist.angle_helper as angle_helper
import matplotlib.cm as cmap
from matplotlib.projections import PolarAxes
from matplotlib.transforms import Affine2D

from mpl_toolkits.axisartist import SubplotHost

from mpl_toolkits.axisartist import GridHelperCurveLinear


def curvelinear_test2(fig, rect=111):
    """
    polar projection, but in a rectangular box.
    """

    # see demo_curvelinear_grid.py for details
    tr = Affine2D().translate(0,90) + Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform()

    extreme_finder = angle_helper.ExtremeFinderCycle(10, 60,
                                                     lon_cycle = 360,
                                                     lat_cycle = None,
                                                     lon_minmax = None,
                                                     lat_minmax = (-90, np.inf),
                                                     )

    grid_locator1 = angle_helper.LocatorHMS(12) #changes theta gridline count
    tick_formatter1 = angle_helper.FormatterHMS()

    grid_helper = GridHelperCurveLinear(tr,
                                        extreme_finder=extreme_finder,
                                        grid_locator1=grid_locator1,
                                        tick_formatter1=tick_formatter1
                                        )


    ax1 = SubplotHost(fig, rect, grid_helper=grid_helper)

    # make ticklabels of right and top axis visible.
    ax1.axis["right"].major_ticklabels.set_visible(True)
    ax1.axis["top"].major_ticklabels.set_visible(True)
    ax1.axis["bottom"].major_ticklabels.set_visible(True) #Turn off? 
    # let right and bottom axis show ticklabels for 1st coordinate (angle)
    ax1.axis["right"].get_helper().nth_coord_ticks=0
    ax1.axis["bottom"].get_helper().nth_coord_ticks=0



    fig.add_subplot(ax1)

    grid_helper = ax1.get_grid_helper()

    # You may or may not need these - they set the view window explicitly rather than using the
    # default as determined by matplotlib with extreme finder.
    ax1.set_aspect(1.)
    ax1.set_xlim(-4,25) # moves the origin left-right in ax1
    ax1.set_ylim(-3, 30) # moves the origin up-down

    ax1.set_ylabel('Declination')
    ax1.set_xlabel('Ascension')
    ax1.grid(True)
    #ax1.grid(linestyle='--', which='x') # either keyword applies to both
    #ax1.grid(linestyle=':', which='y')  # sets of gridlines

    return ax1,tr


def skip_comments(f):
    '''
    Read lines that DO NOT start with a # symbol.
    '''
    for line in f:
        if not line.strip().startswith('#'):
            yield line

def get_data_bb():
    '''RA, DEC data file.
    '''

    # Path to data file.
    out_file = 'bb_cat.dat'

    # Read data file
    with open(out_file) as f:
        ra, dec = [], []

        for line in skip_comments(f):
            ra.append(float(line.split()[0]))
            dec.append(float(line.split()[1]))

    return ra, dec


import matplotlib.pyplot as plt
fig = plt.figure(1, figsize=(5, 5))
fig.clf()

ax1, tr = curvelinear_test2(fig,121) # tr.transform_point((x, 0)) is always (0,0)
                            # => (theta, r) in but (r, theta) out...             

# Read RA, DEC data from file.
ra, dec = get_data_bb()
out_test = tr.transform(zip(ra, dec))

# Use this block to generate colored points with a colorbar.
cm = plt.cm.get_cmap('RdYlBu_r')
z = np.random.random((len(ra), 1))  # RGB values

SC = ax1.scatter(out_test[:,0], #ax1 is a global
            out_test[:,1],
            marker = 'o',
            c=z,
            cmap=cm,
            lw = 0.,
            zorder=9) #on top of gridlines

# Colorbar
cbar = plt.colorbar(SC, shrink=1., pad=0.1)
cbar.ax.tick_params(labelsize=8)
cbar.set_label('colorbar', fontsize=8)

ax2, tr = curvelinear_test2(fig,122) # tr.transform_point((x, 0)) is always (0,0)
                            # => (theta, r) in but (r, theta) out...             

# Read RA, DEC data from file.
ra, dec = get_data_bb()
out_test = tr.transform(zip(ra, dec))

# Use this block to generate colored points with a colorbar.
cm = plt.cm.get_cmap('RdYlBu_r')
z = np.random.random((len(ra), 1))  # RGB values

SC = ax2.scatter(out_test[:,0], #ax1 is a global
            out_test[:,1],
            marker = 'o',
            c=z,
            cmap=cm,
            lw = 0.,
            zorder=9) #on top of gridlines

# Colorbar
cbar = plt.colorbar(SC, shrink=1., pad=0.1)
cbar.ax.tick_params(labelsize=8)
cbar.set_label('colorbar', fontsize=8)

plt.show()

最终情节:

Picture showing proper subplot

答案 2 :(得分:0)

我认为这可能是 Python 3+ 的问题,现在该行了

out_test = tr.transform(zip(deg_test, r_test))

返回错误:

ValueError: 无法将大小为 1 的数组重塑为形状 (2)

换行

out_test = tr.transform(list(zip(deg_test, r_test)))

修复了问题并允许正确生成绘图。