How to get rid of white slivers between adjecent polygons when plotting a geopandas object? - matplotlib

I am plotting some raster data on a map using geopandas.
Even though all polygons are exactly adjectent, when plotting it appears as if there is space in between them, so it looks like there is a white grid on the plot.
I have tried casting to a different crs, but that didn't change anything. Adding padding to the polygons isn't a great solution, there's nothing wrong with the polygons - I've checked they are exactly adjecent.
Here is an example of runnable code where you can clearly see the problem.
import geopandas as gpd
import matplotlib.pyplot as plt
geometries = gpd.read_file('https://confluence.govcloud.dk/download/attachments/53086340/10x10km-Grid.zip?version=1&modificationDate=1644923591000&api=v2')
geometries.plot()
plt.show()
Which gives this output for me

If you specify the edgecolor parameter to be the same color as the polygons, I believe this will render the way you want.
import geopandas as gpd
geometries = gpd.read_file('https://confluence.govcloud.dk/download/attachments/53086340/10x10km-Grid.zip?version=1&modificationDate=1644923591000&api=v2')
geometries.plot(edgecolor='tab:blue')

Related

Matplotlib: Get Rid of White Border

I want to get rid of the white border when I save my image to a png in python.
I tried plt.box(on=None), plt.axis('off'). I tried setting the figure's 'frameon' parameter to false.
import matplotlib.pyplot as plt
from matplotlib.pyplot import figure
figure(num=None, figsize=(7.965,7.965), dpi=80,facecolor='none',clear=True)
plt.box(on=None)
plt.axis('off')
plt.imshow(Data, cmap='Greys_r', norm=Norm,origin='lower',aspect='auto',interpolation='nearest')
plt.savefig(locationFITSfolder+fitsFile[:-5],transparent=False,bbox=False)
I want there to be no white border to my image. Transparent.
If you change the parameters to the savefig function, you will get the desired output.
Specifically, you must use transparent=True. Note that bbox=False and frameon=False are optional, and only change the width of transparent space around your image.
Adapting from your sample code:
import matplotlib.pyplot as plt
from matplotlib.pyplot import figure
#create sample data
import numpy as np
Data = np.random.random([4,4])
figure(num=None, figsize=(7.965,7.965), dpi=80,facecolor='none',clear=True)
plt.box(on=None)
plt.axis('off')
plt.imshow(Data, cmap='Greys_r',origin='lower',aspect='auto',interpolation='nearest')
plt.savefig(locationFITSfolder+fitsFile[:-5],transparent=True)
(sidenote -- you may wish to use os.path.join, .split, and .splitext for file I/O, instead of slicing string names)
This yields the expected image output: (note that the image has transparent borders when you open it in a new tab or download it).

Draw an ordinary plot with the same style as in plt.hist(histtype='step')

The method plt.hist() in pyplot has a way to create a 'step-like' plot style when calling
plt.hist(data, histtype='step')
but the 'ordinary' methods that plot raw data without processing (plt.plot(), plt.scatter(), etc.) apparently do not have style options to obtain the same result. My goal is to plot a given set of points using that style, without making histogram of these points.
Is that achievable with standard library methods for plotting a given 2-D set of points?
I also think that there is at least one hack (generating a fake distribution which would have histogram equal to our data) and a 'low-level' solution to draw each segment manually, but none of these ways seems favorable.
Maybe you are looking for drawstyle="steps".
import numpy as np; np.random.seed(42)
import matplotlib.pyplot as plt
data = np.cumsum(np.random.randn(10))
plt.plot(data, drawstyle="steps")
plt.show()
Note that this is slightly different from histograms, because the lines do not go to zero at the ends.

Geopandas & Mapplotlib, how do I plot without an outline around any shape?

When I run the code below in a Jupyter Notebook,
I get a map of the world, colored in red.
There are fine white-ish lines between the countries.
Is there a way to plot the world so that all countries
are solid and there's no line in between?
I'm asking, because my real world usecase is a fine grid that
behaves just like the world map: Each grid shape has a fine outline
which I do not want to have in the plot. (Update, since this was asked: The grid shapes will not have the same fill color.
)
import geopandas as gpd
import geoplot as gplt
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
world['total'] = 1
world.plot(column='total', cmap='Set1')
For the grid example, the grid files are at https://opendata-esri-de.opendata.arcgis.com/datasets/3c1f46241cbb4b669e18b002e4893711_0
A simplified example that shows the problem.
sf = 'Hexagone_125_km/Hexagone_125_km.shp'
shp = gpd.read_file(sf)
shp.crs = {'init': 'epsg:4326'}
shp['sum'] = 1 # for example, fill sum with something
shp.plot(figsize=(20,20), column='sum', cmap='gnuplot', alpha=1, legend=True)
The white lines are due to antialiasing. This usually makes the visual more smooth, but leads to white lines in between different shapes. You can turn off anialiasing via
antialiased=False
That has the inevitable drawback of the plot looking pixelated.
An alternative is to give the patches an edge with a certain linewidth. The edges should probably have the same color as the faces, so
edgecolor="face", linewidth=0.4
would be an option. This removes the white lines, but introduces a slight "searing" effect (You'll notice mainly looking at islands like Indonesia or Japan). This will be the more noticable, the smaller the features, so it may be irrelevant for showing a hexbin plot. Still, playing a bit with the linewidth might improve the result further.
Code for reproduction:
import numpy as np; np.random.seed(42)
import geopandas as gpd
import matplotlib.pyplot as plt
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
world['total'] = np.random.randint(0,10, size=len(world))
fig, (ax1, ax2, ax3) = plt.subplots(nrows=3, figsize=(7,10))
world.plot(column='total', cmap='Set1', ax=ax1)
world.plot(column='total', cmap='Set1', ax=ax2, antialiased=False)
world.plot(column='total', cmap='Set1', ax=ax3, edgecolor="face", linewidth=0.4)
ax1.set_title("original")
ax2.set_title("antialiased=False")
ax3.set_title("edgecolor='face', linewidth=0.4")
plt.tight_layout()
plt.savefig("world.png")
plt.show()

Cartopy AzimuthalEquidistant projection: zooming into a region and coastlines

I am trying to plot some data on an AzimuthalEquidistant projection using cartopy. However, it gives me a couple of problems. First the coastlines no longer show for this type of projection. Not sure if this is my code or a Cartopy problem. I also notice that if I use a ccrs.PlateCarree() transform in the pcolormesh command the coastlines do show but then, presumably, my data is on the wrong type of prejection?
Second I would prefer if the axis boarder was circular after plotting the data, is it possible to use set_extent or some similar function to do this?
The code below should reproduce the problems, the circle shows how I would like the boarder to look.
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import matplotlib.patches as mpatches
clat = 55.0
clon = -8.0
lons = np.arange(clon-15,clon+16,0.5)
lats = np.arange(clat-15,clat+16,0.5)
d = np.random.rand(lons.shape[0],lats.shape[0])
trans = ccrs.AzimuthalEquidistant(central_latitude=clat, central_longitude=clon)
ax = plt.axes(projection=trans)
ax.coastlines(resolution='10m')
CB=ax.pcolormesh(lons-0.25, lats-0.25, d.T,
cmap=plt.cm.viridis, alpha=0.5,
transform=trans)#ccrs.PlateCarree())
p1 = mpatches.Circle((clon,clat), radius=15, color='k', lw=5, fill=False,
transform=trans)
ax.add_patch(p1)
If the data you are plotting is in latitude/longitude coordinates then the correct value for the transform keyword is indeed ccrs.PlateCarree(). This is common gotcha for new users. The transform argument tells cartopy what coordinates your data are in, and is completely independent of the projection you want to plot onto.
To make the plot circular you'll need to set the boundary yourself. The Cartopy documentation have a couple of examples of this: http://scitools.org.uk/cartopy/docs/latest/examples/always_circular_stereo.html and http://scitools.org.uk/cartopy/docs/latest/examples/star_shaped_boundary.html.

Figures with lots of data points in matplotlib

I generated the attached image using matplotlib (png format). I would like to use eps or pdf, but I find that with all the data points, the figure is really slow to render on the screen. Other than just plotting less of the data, is there anyway to optimize it so that it loads faster?
I think you have three options:
As you mentioned yourself, you can plot fewer points. For the plot you showed in your question I think it would be fine to only plot every other point.
As #tcaswell stated in his comment, you can use a line instead of points which will be rendered more efficiently.
You could rasterize the blue dots. Matplotlib allows you to selectively rasterize single artists, so if you pass rasterized=True to the plotting command you will get a bitmapped version of the points in the output file. This will be way faster to load at the price of limited zooming due to the resolution of the bitmap. (Note that the axes and all the other elements of the plot will remain as vector graphics and font elements).
First, if you want to show a "trend" in your plot , and considering the x,y arrays you are plotting are "huge" you could apply a random sub-sampling to your x,y arrays, as a fraction of your data:
import numpy as np
import matplotlib.pyplot as plt
fraction = 0.50
x_resampled = []
y_resampled = []
for k in range(0,len(x)):
if np.random.rand() < fraction:
x_resampled.append(x[k])
y_resampled.append(y[k])
plt.scatter(x_resampled,y_resampled , s=6)
plt.show()
Second, have you considered using log-scale in the x-axis to increase visibility?
In this example, only the plotting area is rasterized, the axis are still in vector format:
import numpy as np
import matplotlib.pyplot as plt
x = np.random.uniform(size=400000)
y = np.random.uniform(size=400000)
plt.scatter(x, y, marker='x', rasterized=False)
plt.savefig("norm.pdf", format='pdf')