Cartopy: draw gridline labels, but not the gridlines themselves - python

I can use ax.gridlines() to draw gridlines in Cartopy, and I can control whether the labels are drawn with the draw_labels= kwarg.
However, I want to draw only the labels, and not draw the gridlines. Is there a straightforward way to do that? Or do I need to drop down to "lower-level" Matplotlib functionality?

Yes, You can set the color of the grid lines to none.
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
#ref: https://scitools.org.uk/cartopy/docs/latest/gallery/gridlines_and_labels/gridliner.html#sphx-glr-gallery-gridlines-and-labels-gridliner-py
rotated_crs = ccrs.RotatedPole(pole_longitude=120.0, pole_latitude=70.0)
fig = plt.figure(figsize=(12,6))
ax0 = fig.add_subplot(1,2,1, projection=rotated_crs)
ax0.set_extent([-6, 1, 47.5, 51.5], crs=ccrs.PlateCarree())
ax0.add_feature(cfeature.LAND.with_scale('110m'))
ax0.gridlines(draw_labels=True, dms=True, x_inline=False, y_inline=False)
ax0.set_title('Original',fontsize = 24)
# non-gridlines
ax1 = fig.add_subplot(1,2,2, projection=rotated_crs)
ax1.set_extent([-6, 1, 47.5, 51.5], crs=ccrs.PlateCarree())
ax1.add_feature(cfeature.LAND.with_scale('110m'))
ax1.gridlines(draw_labels=True, dms=True, x_inline=False, y_inline=False,color = "None")
ax1.set_title('Non-gridlines',fontsize = 24)
plt.savefig("grid.png")

Related

Matplotlib colorbar does not draw edges at extremities

When creating a matplotlib colorbar, it is possible to set drawedges to True which separates the colors of the colorbar with black lines. However, when the colorbar is extended using extend='both', the black lines at the extremities do not show up. Is that a bug? Is there a possibility to draw those edges otherwise?
Here is the code:
import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import from_levels_and_colors
my_cmap = mpl.cm.viridis
bounds = np.arange(10)
nb_colors = len(bounds) + 1
colors = my_cmap(np.linspace(100, 255, nb_colors).astype(int))
my_cmap, my_norm = from_levels_and_colors(bounds, colors, extend='both')
plt.figure(figsize=(5, 1))
ax = plt.subplot(111)
cbar = mpl.colorbar.ColorbarBase(ax, cmap=my_cmap, norm=my_norm, orientation='horizontal', drawedges=True)
plt.subplots_adjust(left=0.05, bottom=0.4, right=0.95, top=0.9)
plt.show()
and the figure it gives:
I looked into it from your question and found a way to change the color of the border and vertical lines of the color bar. I used that to change them to red. The result I got was that the extended outline was red, so my guess is that I just pulled the short sides of the normal color bar rectangle to the left and right.
I found this response helpful.
cbar.outline.set_edgecolor('red')
cbar.dividers.set_color('red')
So I think the only way to do this is to add vertical lines.
import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import from_levels_and_colors
my_cmap = mpl.cm.viridis
bounds = np.arange(10)
nb_colors = len(bounds) + 1
colors = my_cmap(np.linspace(100, 255, nb_colors).astype(int))
my_cmap, my_norm = from_levels_and_colors(bounds, colors, extend='both')
plt.figure(figsize=(6, 2))
ax = plt.subplot(111)
cbar = mpl.colorbar.ColorbarBase(ax, cmap=my_cmap, norm=my_norm, orientation='horizontal', drawedges=True)
# update
cbar.outline.set_edgecolor('red')
cbar.dividers.set_color('red')
plt.axvline(max(bounds), color='red', alpha=0.3, linewidth=3.5)
plt.axvline(min(bounds), color='red', alpha=0.3, linewidth=3.5)
plt.subplots_adjust(left=0.05, bottom=0.4, right=0.95, top=0.9)
plt.show()

How to remove the frame and axes around a Cartopy/Matplotlib plot?

All of a sudden, probably after some module update, I get an extra box/frame with x (0,1) and y (0,1) axes around my Cartopy map. How do I remove this?
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
#Set the projection information
proj = ccrs.NorthPolarStereo(true_scale_latitude = 75)
#Create a figure with an axes object on which we will plot. Pass the projection to that axes.
fig, ax = plt.subplots(figsize=(8,6))
ax = plt.axes(projection=proj)
ax.coastlines('10m')
ax.set_extent([-180, 180, 65, 90], crs=ccrs.PlateCarree())
Weird extra frame around the Cartopy plot
I have tried:
ax.axis('off')
right_side = ax.spines["right"]
right_side.set_visible(False)
plt.box(False)
plt.xticks([])
plt.yticks([])
plt.box(on=None)
Any other ideas would be highly appreciated.
This is a similar issue to: How to remove the frame around my Cartopy/Matplotlib plot
My first ever answer here. This clears projection border and won't spill over if you have multiple Axes(ax1, ax2)
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
#Set the projection information
proj = ccrs.NorthPolarStereo(true_scale_latitude=75)
#create fig, add 1 or more subplots and declare projection frameon status
fig = plt.figure(figsize=(8, 6), )
ax = fig.add_subplot(projection=proj, frameon=False)
ax.coastlines('10m')
ax.set_extent([-180, 180, 65, 90], crs=ccrs.PlateCarree())
This solves the issue:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
fig,ax = plt.subplots(figsize=(8,6), subplot_kw={"projection": ccrs.NorthPolarStereo(true_scale_latitude = 75)})
ax.coastlines('10m')
ax.set_extent([-180, 180, 65, 90], crs=ccrs.PlateCarree())

Crossing Dateline with cartopy.io.img_tiles

I'm trying to figure out how to generate a map that crosses the dateline with Cartopy and a terrain from img_tiles. Here is what I have so far:
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import cartopy.feature as cfeature
import cartopy.io.img_tiles as cimgt
import shapely.geometry as sgeom
my_dpi = 96
plt.figure(figsize=(1530/my_dpi, 900/my_dpi), dpi=my_dpi, frameon=False)
plt.subplots_adjust(left=0.0, right=1.0, top=1.0, bottom=0)
ax = plt.axes(projection=ccrs.Mercator(central_longitude=180))
terrain = cimgt.Stamen('terrain-background')
ax.add_image(terrain, 4)
states = cfeature.NaturalEarthFeature('cultural', 'admin_1_states_provinces', '10m', edgecolor='darkblue',facecolor='none')
ax.add_feature(states, linewidth = 0.1, linestyle='-')
# draw box
box = sgeom.box(minx=69, maxx=210, miny=-57, maxy=13.5)
ax.add_geometries([box], ccrs.PlateCarree(), facecolor='coral',
edgecolor='black', alpha=0.5)
# Set extent
ax.set_extent(oceania_coords, crs=ccrs.PlateCarree())
plt.show()
When I draw a box around the region I want to zoom in on, it looks correct.
When I try to ax.set_extent on this range, it seems to set all of the cfeatures correctly but screws up with the img_tiles features.
Is there any way to work around this? Thanks for the help!
I have a solution that is good enough for me, by abutting two subplots with the appropriate ratios and borders turned off. There is a tiny artifact on the seam, but I'm mostly slicing ocean in this frame so I'm ok with it. When I have Russia in the frame, it's more obvious.
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import cartopy.feature as cfeature
import cartopy.io.img_tiles as cimgt
import shapely.geometry as sgeom
import matplotlib.gridspec as gridspec
my_dpi=96
f = plt.figure(figsize=(1530/my_dpi, 900/my_dpi), dpi=my_dpi, frameon=False)
spec = gridspec.GridSpec(ncols=2, nrows=1,width_ratios=[111,30])
plt.subplots_adjust(left=0.0, right=1.0, top=1.0, bottom=0)
ax1 = f.add_subplot(spec[0],projection=ccrs.Mercator(central_longitude=180))
terrain = cimgt.Stamen('terrain-background')
ax1.add_image(terrain, 3)
states = cfeature.NaturalEarthFeature('cultural', 'admin_1_states_provinces', '10m', edgecolor='darkblue',facecolor='none')
ax1.add_feature(states, linewidth = 0.1, linestyle='-')
ax1.set_extent([69, 180, -57, 13.5], crs=ccrs.PlateCarree())
plt.gca().outline_patch.set_visible(False)
ax2 = f.add_subplot(spec[1],projection=ccrs.Mercator(central_longitude=180))
ax2.add_image(terrain, 3)
ax2.add_feature(states, linewidth = 0.1, linestyle='-')
ax2.set_extent([-180,-150, -57, 13.5], crs=ccrs.PlateCarree())
plt.gca().outline_patch.set_visible(False)
plt.subplots_adjust(wspace=0)

Matplotlib 3D: Remove axis ticks & draw upper edge border?

It seems like some of the methods that work for matplotlib 2D might not be working for matplotlib 3D. I'm not sure.
I'd like to remove the tick marks from all axes, and extend the edge color from the bottom and sides to the top as well. The farthest I have gotten is being able to draw the ticks as white, which looks bad as they are rendered on top of the edge lines.
Below is a big chunk of self-contained code that results in the following image. Any help is much appreciated!
import numpy as np
import matplotlib as mpl
from matplotlib import pyplot as plt
from matplotlib import animation
from mpl_toolkits.mplot3d import Axes3D
mpl.rcParams['ytick.color'] = 'white'
#mpl.rcParams['ytick.left'] = False
sample = np.random.random_integers(low=1,high=5, size=(10,3))
# Create a figure and a 3D Axes
fig = plt.figure(figsize=(5,5))
ax = Axes3D(fig)
#ax.w_xaxis.set_tick_params(color='white')
#ax.axes.tick_params
ax.axes.tick_params(bottom=False, color='blue')
##['size', 'width', 'color', 'tickdir', 'pad', 'labelsize',
##'labelcolor', 'zorder', 'gridOn', 'tick1On', 'tick2On',
##'label1On', 'label2On', 'length', 'direction', 'left', 'bottom',
##'right', 'top', 'labelleft', 'labelbottom',
##'labelright', 'labeltop', 'labelrotation']
colors = np.mean(sample[:, :], axis=1)
ax.scatter(sample[:,0], sample[:,1], sample[:,2],
marker='o', s=20, c=colors, alpha=1)
ax.tick_params(color='red')
frame1 = plt.gca()
frame1.axes.xaxis.set_ticklabels([])
frame1.axes.yaxis.set_ticklabels([])
frame1.axes.zaxis.set_ticklabels([])
#frame1.axes.yaxis.set_tick_params(color='white')
To answer the first bit of the question, about tick removal,
it's probably easiest to just disable the tick lines:
for line in ax.xaxis.get_ticklines():
line.set_visible(False)
for line in ax.yaxis.get_ticklines():
line.set_visible(False)
for line in ax.zaxis.get_ticklines():
line.set_visible(False)
E.g.:
import numpy as np
import matplotlib as mpl
from matplotlib import pyplot as plt
from matplotlib import animation
from mpl_toolkits.mplot3d import Axes3D
sample = np.random.random_integers(low=1,high=5, size=(10,3))
# Create a figure and a 3D Axes
fig = plt.figure(figsize=(5,5))
ax = Axes3D(fig)
colors = np.mean(sample[:, :], axis=1)
ax.scatter(sample[:,0], sample[:,1], sample[:,2],
marker='o', s=20, c=colors, alpha=1)
ax = plt.gca()
ax.xaxis.set_ticklabels([])
ax.yaxis.set_ticklabels([])
ax.zaxis.set_ticklabels([])
for line in ax.xaxis.get_ticklines():
line.set_visible(False)
for line in ax.yaxis.get_ticklines():
line.set_visible(False)
for line in ax.zaxis.get_ticklines():
line.set_visible(False)
For newer versions (e.g. matplotlib 3.5.1) a lot of formatting can be done via mpl_toolkits.mplot3d.axis3d._axinfo:
import numpy as np
from matplotlib import pyplot as plt
sample = np.random.randint(low=1,high=5, size=(10,3))
# Create a figure and a 3D Axes
fig = plt.figure(figsize=(5,5))
ax = fig.add_subplot(projection='3d')
colors = np.mean(sample[:, :], axis=1)
ax.scatter(sample[:,0], sample[:,1], sample[:,2],
marker='o', s=20, c=colors, alpha=1)
for axis in [ax.xaxis, ax.yaxis, ax.zaxis]:
axis.set_ticklabels([])
axis._axinfo['axisline']['linewidth'] = 1
axis._axinfo['axisline']['color'] = (0, 0, 0)
axis._axinfo['grid']['linewidth'] = 0.5
axis._axinfo['grid']['linestyle'] = "-"
axis._axinfo['grid']['color'] = (0, 0, 0)
axis._axinfo['tick']['inward_factor'] = 0.0
axis._axinfo['tick']['outward_factor'] = 0.0
axis.set_pane_color((0.95, 0.95, 0.95))
plt.show()

How to easily add a sub_axes with proper position and size in matplotlib and cartopy?

I want to add a 2nd axes at the top right corner of a 1st axes. After googling, I found two ways to do things like this: fig.add_axes(), and mpl_toolkits.axes_grid.inset_locator.inset_axes. But the fig.add_axes() doesn't accept transform arg. So the following code throws an error. So the position can't be under the parent axes coordinates but the figure coordinates.
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
fig, ax = plt.subplots(1, 1, subplot_kw={'projection': ccrs.PlateCarree()})
ax2 = fig.add_axes([0.8, 0, 0.2, 0.2], transform=ax.transAxes, projection=ccrs.PlateCarree())
And inset_axes() doesn't accept the projection arg, so I can't add ax2 as a cartopy geo-axes.
from mpl_toolkits.axes_grid.inset_locator import inset_axes
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
fig, ax = plt.subplots(1, 1, subplot_kw={'projection': ccrs.PlateCarree()})
# The following line doesn't work
ax2 = inset_axes(ax, width='20%', height='20%', axes_kwargs={'projection': ccrs.PlateCarree()})
# Doesn't work neither:
ax2 = inset_axes(ax, width='20%', height='20%', projection=ccrs.PlateCarree())
I've asked the question at matplotlib issue. It seems the following code works well as long as it's not a cartopy axes.
import matplotlib as mpl
fig, ax = plt.subplots(1, 1)
box = mpl.transforms.Bbox.from_bounds(0.8, 0.8, 0.2, 0.2)
ax2 = fig.add_axes(fig.transFigure.inverted().transform_bbox(ax.transAxes.transform_bbox(box)))
Question:
How to easily add a sub_axes with proper position and size in matplotlib and cartopy?
As I understand, after ax.set_extend(), the size of axes will change. So maybe is there a way that some point of sub_axes (eg: top right corner of ax2) can be anchored at one fixed position of the parent_axes (eg: top right corner of ax1)?
As inset_axes() doesn't accept projection arg, the roundabout way is to use InsetPosition(). This way you can create an axes in the usual way (using projection), and then "link" both axes using InsetPosition(). The main advantage over using subplots or similar is that the inset position is fixed, you can resize the figure or change the main plot area and the inset will always be in the same place relative to the main axes. This was based on this answer: specific location for inset axes, just adding the cartopy way of doing things.
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from mpl_toolkits.axes_grid1.inset_locator import InsetPosition
from shapely.geometry.polygon import LinearRing
extent = [-60, -30, -40, -10]
lonmin, lonmax, latmin, latmax = extent
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())
ax.set_extent(extent, crs=ccrs.PlateCarree())
ax.add_feature(cfeature.LAND)
ax.add_feature(cfeature.OCEAN)
ax.add_feature(cfeature.COASTLINE)
# inset location relative to main plot (ax) in normalized units
inset_x = 1
inset_y = 1
inset_size = 0.2
ax2 = plt.axes([0, 0, 1, 1], projection=ccrs.Orthographic(
central_latitude=(latmin + latmax) / 2,
central_longitude=(lonmin + lonmax) / 2))
ax2.set_global()
ax2.add_feature(cfeature.LAND)
ax2.add_feature(cfeature.OCEAN)
ax2.add_feature(cfeature.COASTLINE)
ip = InsetPosition(ax, [inset_x - inset_size / 2,
inset_y - inset_size / 2,
inset_size,
inset_size])
ax2.set_axes_locator(ip)
nvert = 100
lons = np.r_[np.linspace(lonmin, lonmin, nvert),
np.linspace(lonmin, lonmax, nvert),
np.linspace(lonmax, lonmax, nvert)].tolist()
lats = np.r_[np.linspace(latmin, latmax, nvert),
np.linspace(latmax, latmax, nvert),
np.linspace(latmax, latmin, nvert)].tolist()
ring = LinearRing(list(zip(lons, lats)))
ax2.add_geometries([ring], ccrs.PlateCarree(),
facecolor='none', edgecolor='red', linewidth=0.75)
I may have figured something out.
According to the answer this question. I can get the position of both axes, then reposition the 2nd axes. The code was like:
import matplotlib.pyplot as plt
from cartopy import crs as ccrs
fig, ax = plt.subplots(1, 1, subplot_kw={'projection': ccrs.PlateCarree()})
ax2 = fig.add_axes([0.8, 0.8, 0.2, 0.2], projection=ccrs.PlateCarree())
ax.set_extent([100, 120, 20, 40])
ax.coastlines()
ax2.set_global()
ax2.coastlines()
ax2.stock_img()
def reposition():
plt.draw()
p1 = ax.get_position()
p2 = ax2.get_position()
ax2.set_position([p1.x1-p2.width, p1.y1-p2.height, p2.width, p2.height])
reposition()
plt.show()
The result is just what I want.

Categories