3

I have tried to create a polar plot using matplotlib's contourf, but it produces some not-filled areas near the circle boundary, in just some sides of the plot. It seems, this problem is a common problem that we can see in many examples e.g. 1.

enter image description here

At first, I thought it may needs some interpolations and tried interpolating based on some available methods e.g. 2. But I couldn't produce a perfect plot. Interpolating using SciPy griddata with linear method solved the main issue but produce some shadows on the plot, and the cubic method result in some inappropriate colors (which shown the results incorrect).

enter image description here

Finally, I guess this issue may be related to figure size that I specified and the dpi that I used. With low dpi it was cured a lot, but will get a low quality png. when I deactivate the related line for specifying figure size (# plt.rcParams["figure.figsize"] = (19.2, 9.6)) with dpi=600, it is shown fairly correct, but not on the needed figure size.

enter image description here

The main question is how to solve the issue for saved files, with the desired specified figure size and dpi? It must be said that the problem is appearing on the saved files.

Besides the answer to solve the issue, I will be appreciated if any answer about these questions too:

  • Why it happens?

  • Why it happens just in some sides of the plot? In this example it is problematic just on the right side of the quarter, not the upside. This issue makes me doubt that the data is correctly shown on the circle for analysis. Is it OK?

  • Do we need interpolating on such data? If so, which algorithms will be the best for that which does not show the results incorrectly as cubic method in Scipy interpolation and without shadowing as the linear method? If choosing between algorithms is based on the case, how to decide for that? It will be very helpful if be explained with examples.

import numpy as np
from matplotlib import cm
import matplotlib.pyplot as plt


plt.rcParams["figure.figsize"] = (19.2, 9.6)
save_dpi = 600

Azimuth = np.tile(np.arange(0, 91, 10), 10)
Deviation = np.repeat(np.arange(0, 91, 10), 10)
color_data = np.array([2123, 2124, 2126, 2130, 2135, 2139, 2144, 2147, 2150, 2151, 2212,
                       2211, 2205, 2197, 2187, 2176, 2166, 2158, 2152, 2150, 2478, 2468,
                       2439, 2395, 2342, 2285, 2231, 2188, 2160, 2150, 2912, 2888, 2819,
                       2715, 2589, 2456, 2334, 2236, 2172, 2150, 3493, 3449, 3324, 3135,
                       2908, 2674, 2462, 2294, 2187, 2150, 4020, 4020, 3912, 3618, 3270,
                       2917, 2602, 2357, 2203, 2151, 4020, 4020, 4020, 4020, 3633, 3156,
                       2737, 2417, 2218, 2150, 4020, 4020, 4020, 4020, 3947, 3358, 2850,
                       2466, 2230, 2150, 4020, 4020, 4020, 4020, 4020, 3495, 2926, 2499,
                       2238, 2150, 4020, 4020, 4020, 4020, 4020, 3543, 2951, 2510, 2241,
                       2150])

ax = plt.subplot(projection='polar')
plt.xticks([])
plt.yticks([])

Az = np.unique(Azimuth)
Dev = np.unique(Deviation)

mAz, mDev = np.meshgrid(Az, Dev)

# way1: Original
Xi, Yi, Zi = np.deg2rad(mAz), mDev, color_data.reshape(mAz.shape)
contour_plot = ax.contourf(Xi, Yi, Zi, levels=256, cmap=cm.viridis_r, zorder=1)
ax.plot()
plt.savefig("way1.png", dpi=save_dpi)

# way2: Interpolation
# import scipy.interpolate as sci_int
# Xi = np.linspace(0, 2 * np.pi, 256, endpoint=True)
# Yi = np.linspace(0, 90, 256, endpoint=True)
# Zi = sci_int.griddata(np.stack((np.deg2rad(mAz), mDev), axis=2).reshape(len(Azimuth), 2), color_data,
#                                                                             (Xi[None, :], Yi[:, None]), method='linear')
# contour_plot = ax.contourf(Xi, Yi, Zi, levels=256, cmap=cm.viridis_r, zorder=1)
# ax.plot()
# plt.savefig("way2.png", dpi=save_dpi)

Tested on windows 10 by:
Python ver.: 3.8 & 3.10
Matplotlib ver.: 3.5.3 & 3.7.2

Ali_Sh
  • 2,667
  • 3
  • 43
  • 66
  • Possible workaround, don't know if it works: draw a black circle with a larger line width after plotting the meshgrid. – gboffi Jul 15 '23 at 20:44
  • 2
    @gboffi , Solutions without changing the current characteristics of the plot is of the most interest; As much as possible. Beside, I think applicability of such workaround will be data-sensitive, i.e. perhaps be applicable just on examples with small non-filled parts as the prepared one in this post; I saw some examples which have larger voids that this trick was not a good choice. Perhaps using photoshop be a better choice to fill the non-filled areas rather than using a thicker circle. However, I doubt the authenticity of the work due to occurrence of this issue in just one side, too. – Ali_Sh Jul 16 '23 at 08:37
  • Have you tried anything else? What about different Interpolation then the ones you mentioned? Have you tried nearest, shepard or spline? Have you tried experimenting with the levels param in contourf? It may never be perfect – Lucas Hendren Jul 21 '23 at 11:27
  • @LucasHendren , I have tested different levels before without any improvements, even with some other issues; e.g. [linear interpolation with level 1000](https://drive.google.com/file/d/1sGFCXdqXF3kWTbHhPv6rZ2eeQBwzu9Vn/view?usp=sharing) and [another example](https://drive.google.com/file/d/159FF6yxsDqQabYnYU7wr2uaCBMvO3IVX/view?usp=sharing), and also, [the nearest method](https://drive.google.com/file/d/1u_SVEtmhqleUkXjBtgYXWZDdJAX_c055/view?usp=sharing) which get a plot like *pcolormesh* styles using *sci_int.griddata*'s methods for interpolations; but not the *shepard* or *spline* methods. – Ali_Sh Jul 21 '23 at 12:41
  • Perhaps a clue that might help you in figuring this out is to switch the output to .pdf. I did this with your code and the contourf division of "filled in" shapes becomes a lot more obvious (and you can also change the `levels` parameter and notice too where exactly contourf is "filling" in more clearly). For some reason it is not dividing the lower right portion of that upper right quadrant of the polar graph into as many sections to 'fill'. – John Collins Jul 22 '23 at 03:05
  • @JohnCollins , I opened [an issue in GitHub](https://github.com/matplotlib/matplotlib/issues/26286), too; The reason is explained/clarified there. – Ali_Sh Jul 25 '23 at 21:57

1 Answers1

1

After a related discussion in matplotlib repo, it seems there is not any simple and conventional solution for that (perhaps cartopy have prepared something helpful); The contouring algorithm doesn't know that it is acting on a polar plot, so when the contours are drawn in polar space the polygons are approximated.

This isn't actually a contouring issue, it is an issue of whether a straight line in one coordinate system should be transformed to a straight or non-straight line in another coordinate system. It applies equally to any polygon (e.g. just a simple triangle) specified in, for example, polar coordinates, and rendered in cartesian (screen) coordinates.

Just for a sub-solution, I tried to produce more points (Azimuth & Deviation) and interpolate their corresponding color_data (the more points you have, the shorter the lines are and the closer this looks to a circular segment) to reduce/cover this shortcoming. In this regard, and for this example, Radial basis function (RBF) interpolation of SciPy get an acceptable answer with the following code (which could be developed/adjusted for such problems):

from scipy.interpolate import RBFInterpolator

coordinates_org = np.column_stack((Azimuth, Deviation))
Az = np.linspace(0, 90, 181, endpoint=True)
Dev = np.linspace(0, 90, 181, endpoint=True)
Az_2D, Dev_2D = np.meshgrid(Az, Dev)
coordinates_int = np.column_stack((Az_2D.ravel(), Dev_2D.ravel()))
rbf = RBFInterpolator(coordinates_org, color_data, kernel="linear")
color_data_int = rbf(coordinates_int).reshape(len(Az), len(Dev))
contour_plot = ax.contourf(np.deg2rad(Az), Dev, color_data_int, levels=1000, cmap=cm.viridis_r)
ax.plot()
plt.savefig("way3.png", dpi=save_dpi)

enter image description here

I adjust this solution for another example by some modifications to compare its results with some various interpolation methods, which can be seen below. In this comparison, unfilled area above the circles for griddata might be filled using other levels than used, which was 1000 as I remember. Based on the comparison, this solution (RBF) produces better results than using griddata as way 2 in the question; Also, quintic method seems got the best:

enter image description here

Ali_Sh
  • 2,667
  • 3
  • 43
  • 66