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()
Related
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")
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())
When plotting data using pcolormesh on a basemap projection (or a cartopy projection) I notice strange lines appear when I set the alpha value to less than 1.
Example code:
from mpl_toolkits.basemap import Basemap
import numpy as np
import matplotlib.pyplot as plt
plt.clf()
dpp =1 # degrees per pixel
lons = np.arange(-180,180+dpp,dpp)
lats = -1*np.arange(-90,90+dpp,dpp)
m = Basemap(projection='robin',lon_0=0)
data = np.random.random((np.size(lats), np.size(lons)))
lons, lats = np.meshgrid(lons, lats)
x, y = m(lons, lats)
im = m.pcolormesh(x, y, x, latlon=False, cmap='RdBu')
#im = m.pcolormesh(lons, lats, data, latlon=True, cmap='RdBu')
m.colorbar(im)
plt.show()
The output shows strange lines appearing:
If I instead set alpha=1 the lines disappear and the behavior is as expected:
Any ideas on how to get pcolormesh to work with a nonzero alpha value?
Use pcolor instead of pcolormesh, it is a bit slower but it does a better job with handling rasterized output. Be sure to set snap = True, this will align the grid to the pixels.
Example
import numpy as np
import matplotlib.pyplot as plt
lons, lats = np.meshgrid(np.arange(-180,180), np.arange(90,-90,-1))
im = plt.pcolor(lons, lats, lons, cmap='RdBu', alpha=0.5, snap=True)
cbar = plt.colorbar(im)
cbar.set_alpha(0.5)
plt.show()
This should work with mpl_toolkits.basemap as well.
The lines in the colorbar are caused by the open issue #1188, as far as I know there is not a work around known which does not involve manually creating the colorbar.
Since it is a global map, I got it to work using imshow instead of pcolor or pcolormesh:
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
plt.clf()
lons, lats = np.meshgrid(np.arange(-180,180), np.arange(90,-90,-1))
im = ax.imshow(lons, transform=ccrs.PlateCarree(),cmap='RdBu', alpha=0.5, extent=[-180,180,-90,90])
cbar = plt.colorbar(im)
cbar.set_alpha(0.5)
plt.show()
There is still the issue with the colorbar however.
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."
I have need to plot point on the map, produce PNG image on that map and, output display coordinated of the plotted point.
Using cartopy I could get the map I wanted and plot a point in given lon/lat coordinates.
I cannot figure out how to get the pixel coordinates out. I tried to follow simple matplotlib tutorial https://matplotlib.org/users/transforms_tutorial.html But it does not work as expected in this situation
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
# Create Mercator projection with dateline in the middle:
from matplotlib import lines
fig = plt.figure(figsize=(10, 10))
ax = plt.axes(projection=ccrs.Mercator(central_longitude=26,))
ax.set_extent([19, 33, 59.5, 70.5], crs=ccrs.PlateCarree())
LAND = cfeature.NaturalEarthFeature('physical', 'land', '50m',
edgecolor='face',
facecolor=cfeature.COLORS['land'], zorder=-1)
ax.add_feature(LAND)
ax.coastlines(resolution='50m')
ax.add_feature(cfeature.NaturalEarthFeature('cultural', 'admin_0_boundary_lines_land',
'50m', edgecolor='black', facecolor='none'))
plt.plot([26.7042], [60.8679], color='blue', linewidth=2, marker='o',
transform=ccrs.PlateCarree(),
)
fig.canvas.draw()
# print image x y coordinates of point 60.8679° N, 26.7042° E here
plt.show()