cartopy set extent with central_longitude=180 - python

Cartopy 0.17.0:
When I set central_longitude, I don't know how to set the extents exactly provided:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
projection = ccrs.PlateCarree(central_longitude=180)
ax = plt.axes(projection=projection)
ax.coastlines()
ax.set_extent((-120, 120, -45, 45), crs=ccrs.PlateCarree())
ax.gridlines(draw_labels=True, crs=ccrs.PlateCarree())
This subsets latitudes correctly:
This subsets longitudes correctly, but has extra labels:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
projection = ccrs.PlateCarree(central_longitude=180)
ax = plt.axes(projection=projection)
ax.coastlines()
ax.set_extent((-120, 120, -45, 45))
ax.gridlines(draw_labels=True, crs=ccrs.PlateCarree())
This sets latitudes correctly:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
projection = ccrs.PlateCarree(central_longitude=180)
ax = plt.axes(projection=projection)
ax.coastlines()
ax.set_extent((-120, 120, -45, 45), crs=projection)
ax.gridlines(draw_labels=True, crs=ccrs.PlateCarree())

Using Cartopy to plot map across world dateline is not simple as you have found. It needs some tricks to get it right. The most important thing is the CRS that must be used correctly in all parts of your code.
Code:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
# cartopy-0.17.0 pyshp-2.1.0
cm = 180
proj = ccrs.PlateCarree(central_longitude=cm)
fig = plt.figure(figsize=[5, 8])
ax = fig.add_subplot(1, 1, 1, projection=proj)
ax.coastlines()
# original ax.set_extent((-120, 120, -45, 45)) ?
# Need longitude extent from -60 to +60 on PlateCarree(central_longitude=180)
minlon = -60 + cm
maxlon = +60 + cm
ax.set_extent([minlon, maxlon, -45, 45], ccrs.PlateCarree())
ax.gridlines(draw_labels=True, crs=proj)
plt.show()
Output plot1, with longitude labels in PlateCarree(central_longitude=180) which is natural in itself, but not geographic norm.
If you want to have ordinary geographic longitude labels in the plot above, you can't simply use
ax.gridlines(draw_labels=True, crs=PlateCarree())
in the code, as you have found.
Output plot2, with ordinary geographic longitude labels
This requires specific instruction in ax.gridlines() as follows:
ax.gridlines(draw_labels=False, crs=ccrs.PlateCarree(), xlocs=[120,140,160,180,200,220,240])
ax.gridlines(draw_labels=True, crs=ccrs.PlateCarree(), xlocs=[120,140,160,180,-160,-140,-120])
Hope this is useful to all readers.

It's depend with 'class PlateCarree' properties (and properties of the CylindricalProjection which is used).
Please see the documentation.
Longitude value 180 is a border.
If set extent [120 180 ...] or [-120 180 ...] then there are no problems.
I think make a sense try other projection.

For example:
projection = ccrs.LambertCylindrical(central_longitude=180)
Unfortunately, "Cannot label Lambert Cylindrical grid lines. Only PlateCarree gridlines are currently supported."

Related

Cartopy: draw gridline labels, but not the gridlines themselves

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")

When using Cartopy (Python) to make Orthographic plot, how to crop the map / set the latitude/longitude boundaries of image?

I'm plotting an orthographic projection of the north pole next to one of the south pole. However, I want to crop the images so that I only see latitudes within maybe 10 or degrees of the poles, rather than all the way down to the equator. Here is my code so far:
# %% Import packages
import os
import matplotlib.pyplot as plt
import sys
import pandas as pd
import numpy as np
from geopack import geopack
import cartopy.crs as ccrs
import cartopy.feature as cfeature
fig = plt.figure(figsize=(20,10))
# North
ax = fig.add_subplot(1,2,1, projection=ccrs.Orthographic(central_latitude=90))
ax.stock_img()
ax.coastlines()
ax.add_feature(cfeature.BORDERS, linestyle="--")
# South
ax = fig.add_subplot(1,2,2, projection=ccrs.Orthographic(central_latitude=-90))
ax.stock_img()
ax.coastlines()
ax.add_feature(cfeature.BORDERS, linestyle="--")
plt.show()
The plot that my code currently generates
Any help would be appreciated. Thanks!
Map zooming may not be supported by the Orthographic projection in Cartopy.
I try the South[North]PolarStereo() projection, using set_extent to limit the map region, and clipping the plot by a circular path. The output figrure may meet your requirements.
Here is the code.
# %% Import packages
#import os
import matplotlib.pyplot as plt
#import sys
#import pandas as pd
import numpy as np
#from geopack import geopack
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.path as mpath
# ref: https://scitools.org.uk/cartopy/docs/latest/gallery/lines_and_polygons/always_circular_stereo.html#sphx-glr-gallery-lines-and-polygons-always-circular-stereo-py
def add_circle_boundary(ax):
# Compute a circle in axes coordinates, which we can use as a boundary
# for the map. We can pan/zoom as much as we like - the boundary will be
# permanently circular.
theta = np.linspace(0, 2*np.pi, 100)
center, radius = [0.5, 0.5], 0.5
verts = np.vstack([np.sin(theta), np.cos(theta)]).T
circle = mpath.Path(verts * radius + center)
ax.set_boundary(circle, transform=ax.transAxes)
fig = plt.figure(figsize=(20,10))
# South
#ax = fig.add_subplot(1,2,1, projection=ccrs.Orthographic(central_latitude=90))
#ax = fig.add_subplot(1,2,1, projection=ccrs.NorthPolarStereo())
ax = fig.add_subplot(1,2,1, projection=ccrs.SouthPolarStereo())
ax.stock_img()
ax.coastlines()
ax.gridlines()
ax.set_title('Original',fontsize = 24)
#ax.add_feature(cfeature.BORDERS, linestyle="--")
ax.set_extent([-180, 180, -90, 0], crs=ccrs.PlateCarree())
add_circle_boundary(ax)
# South zoomed
ax1 = fig.add_subplot(1,2,2, projection=ccrs.SouthPolarStereo())
ax1.stock_img()
ax1.coastlines()
ax1.gridlines()
#ax1.add_feature(cfeature.BORDERS, linestyle="--")
ax1.set_extent([-180, 180, -90, -60], crs=ccrs.PlateCarree())
ax1.set_title('Zoomed',fontsize = 24)
add_circle_boundary(ax1)
#plt.show()
plt.savefig("out.png")

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)

Cartopy projection changing with different data / unexpected behaviour

I am migrating from basemap to cartopy so I'm still trying to find the ropes! If I plot some data on a projection, I would expect it to plot the map then the data on top, like this
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import numpy as np
latitude = np.linspace(-90, 90, 180)
longitude = np.linspace(-180,180,360)
data = np.cos(np.deg2rad(latitude[:, np.newaxis])) + np.sin(np.deg2rad(longitude))
crs = ccrs.Orthographic(-30,45) #some sample data
ax1 = plt.subplot(121,projection=crs)
ax1.contourf(longitude, latitude,data, transform=ccrs.PlateCarree())
ax1.coastlines('110m', edgecolor='black', linewidth=0.75)
This will produce a true orographic projection with the data. But if I make some of the data void/nan, it no longer plots over the same domain:
data[0:150,:] = np.nan
ax1 = plt.subplot(122,projection=crs)
ax1.contourf(longitude, latitude,data, transform=ccrs.PlateCarree())
ax1.coastlines('110m', edgecolor='black', linewidth=0.75)
plt.show()
From basemap, I would expect the same domain to be plotted, but where data is nan it would just be white. How do I force cartopy to stop 'auto-adjusting' to the data, and to keep the same projection?
Image of how data[0:150,:] = np.nan affects the projection below.
Thanks in advance! I tried to force it with ax1.set_extent([-180, 180, -90, 90], ccrs.PlateCarree()) but to no avail.
Cartopy always tries to fit the domain to the data extents. If you want to see a global plot the use ax1.set_global() to force this:
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import numpy as np
latitude = np.linspace(-90, 90, 180)
longitude = np.linspace(-180,180,360)
data = np.cos(np.deg2rad(latitude[:, np.newaxis])) + np.sin(np.deg2rad(longitude))
crs = ccrs.Orthographic(-30,45) #some sample data
ax1 = plt.subplot(121,projection=crs)
ax1.contourf(longitude, latitude,data, transform=ccrs.PlateCarree())
ax1.coastlines('110m', edgecolor='black', linewidth=0.75)
data[0:150,:] = np.nan
ax1 = plt.subplot(122,projection=crs)
ax1.contourf(longitude, latitude,data, transform=ccrs.PlateCarree())
ax1.coastlines('110m', edgecolor='black', linewidth=0.75)
ax1.set_global()
plt.show()

Categories