Подтвердить что ты не робот

Остановить pyplot.contour от рисования контура вдоль разрыва

У меня есть 2d-карта преобразования координат. Данные в каждой точке представляют собой азимутальный угол в исходной системе координат, который идет от 0 до 360. Я пытаюсь использовать pyplot.contour для построения линий постоянного угла, например. 45 градусов. Контур появляется вдоль линии 45 градусов между двумя полюсами, но есть дополнительная часть к контуру, который соединяет два полюса вдоль разрыва 0/360. Это делает очень зубчатую уродливую линию, поскольку она в основном просто прослеживает пиксели с числом, близким к 0 с одной стороны, а другое близко к 360 на другом.

Примеры: Вот изображение с использованием полноцветной карты: colour map with discontinuity

Вы можете видеть разрывы вдоль синей/красной кривой на левой стороне. Одна сторона - 360 градусов, другая - 0 градусов. При построении контуров я получаю:

contour plot with discontinuity

Обратите внимание, что все контуры соединяют два полюса, но даже если я НЕ построил контур 0 градусов, все остальные контуры следуют вдоль разрыва 0 градусов (потому что кузнец думает, если он 0 с одной стороны и 360 - с другой, между ними должны быть все другие углы).

Код для создания этих данных:

import numpy as np
import matplotlib.pyplot as plt
jgal = np.array( [[-0.054875539726,-0.873437108010,-0.483834985808],\
                 [0.494109453312,-0.444829589425, 0.746982251810],\
                 [-0.867666135858,-0.198076386122, 0.455983795705]])

def s2v3(rra, rdec, r):
    pos0 = r * np.cos(rra) * np.cos(rdec)
    pos1 = r * np.sin(rra) * np.cos(rdec)
    pos2 = r * np.sin(rdec)
    return np.array([pos0, pos1, pos2])

def v2s3(pos):
    x = pos[0]
    y = pos[1]
    z = pos[2]
    if np.isscalar(x): x, y, z = np.array([x]), np.array([y]), np.array([z])
    rra = np.arctan2(y, x)
    low = np.where(rra < 0.0)
    high = np.where(rra > 2.0 * np.pi)
    if len(low[0]): rra[low] = rra[low] + (2.0*np.pi)
    if len(high[0]): rra[high] = rra[high] - (2.0*np.pi)
    rxy = np.sqrt(x**2 + y**2)
    rdec = np.arctan2(z, rxy)
    r = np.sqrt(x**2 + y**2 + z**2)
    if x.size == 1:
        rra = rra[0]
        rdec = rdec[0]
        r = r[0]
    return rra, rdec, r


def gal2fk5(gl, gb):
    dgl = np.array(gl)
    dgb = np.array(gb)
    rgl = np.deg2rad(gl)
    rgb = np.deg2rad(gb)
    r = 1.0
    pos = s2v3(rgl, rgb, r)

    pos1 = np.dot(pos.transpose(), jgal).transpose()

    rra, rdec, r = v2s3(pos1)

    dra = np.rad2deg(rra)
    ddec = np.rad2deg(rdec)

    return dra, ddec


def make_coords(resolution=50):
    width=9
    height=6
    px = width*resolution
    py = height*resolution
    coords = np.zeros((px,py,4))
    for ix in range(0,px):
        for iy in range(0,py):
            l = 360.0/px*ix - 180.0
            b = 180.0/py*iy - 90.0
            dra, ddec = gal2fk5(l,b)
            coords[ix,iy,0] = dra
            coords[ix,iy,1] = ddec
            coords[ix,iy,2] = l
            coords[ix,iy,3] = b
    return coords

 coords = make_coords()

 # now do one of these
 #plt.imshow(coords[:,:,0],origin='lower') # color plot
 #plt.contour(coords[:,:,0],levels=[45,90,135,180,225,270,315]) # contour plot with jagged ugliness 

Как я могу:

  • остановить pyplot.contour от рисования контура вдоль разрыва

  • make pyplot.contour признают, что разрыв 0/360 в угле не является реальным разрывом вообще.

Я могу просто увеличить разрешение базовых данных, но до того, как я получу приятную плавную линию, начинает занять очень много времени и много памяти для построения.

Я также хочу построить контур вдоль 0 градусов, но если я смогу понять, как скрыть разрыв, я могу просто перенести его в другое место, не находящееся рядом с контуром. Или, если я могу сделать # 2, это не будет проблемой.

4b9b3361

Ответ 1

Это, безусловно, еще взломать, но вы можете получить приятные гладкие контуры с двукратным подходом:

  • Графики контуров абсолютного значения фазы (от -180 ° до 180 °), так что нет разрыва.
  • Разделите два набора контуров в конечной области, чтобы числовые дефекты, близкие к вершинам и основаниям экстремумов, не ползали.

Вот полный код для добавления к вашему примеру:

Z = np.exp(1j*np.pi*coords[:,:,0]/180.0)
Z *= np.exp(0.25j*np.pi/2.0)   # Shift to get same contours as in your example
X = np.arange(300)
Y = np.arange(450)

N = 2
levels = 90*(0.5 + (np.arange(N) + 0.5)/N)
c1 = plt.contour(X, Y, abs(np.angle(Z)*180/np.pi), levels=levels)
c2 = plt.contour(X, Y, abs(np.angle(Z*np.exp(0.5j*np.pi))*180/np.pi), levels=levels)

Smooth contour plot of phase angle

Можно обобщить этот код, чтобы получить гладкие контуры для любой "периодической" функции. Остается только создать новый набор контуров с правильными значениями, чтобы цветовые схемы применялись правильно, метки будут применяться правильно и т.д. Однако, похоже, нет простого способа сделать это с помощью matplotlib: соответствующий QuadContourSet класс делает все, и я не вижу простого способа построения соответствующего контурного объекта из контуров c1 и c2.