Aligning data (contourf) on Basemap - python
I've started working with Basemap, which seems potentially very useful.
If I plot some global data on a latitude/longitude grid as filled contours, it works great: Iff I leave the lat_0 and lon_0 as zero. Once I change the center location, the map moves but the data doesn't. I would be grateful for advice.
I've created a simple version of the code I'm using, with some simple sample data that illustrates the problem. The values should be (are) large at the equator but small at the poles. If you run the code with lat_0 and lon_0 = 0, it works fine. But if you change the center location to a different coordinate, the same pattern/data is presented even though the map has moved.
from mpl_toolkits.basemap import Basemap, cm
import matplotlib.pyplot as plt
import numpy as np
# create data
lat = np.linspace(-90,90,num=180)
lon = np.linspace(-180,180,num=361)
h2o_north = np.linspace(1,65,num=90)
h2o_south = np.flipud(h2o_north)
h2o = np.append(h2o_north,h2o_south)
data = np.transpose(np.tile(h2o,(len(lon),1)))
# create figure and axes instances
fig = plt.figure(figsize=(10,10))
ax = fig.add_axes([0.1,0.1,0.8,0.8])
# create map
m = Basemap(projection='ortho',lon_0=-50,lat_0=50,resolution='l')
# draw coastlines and country boundaries
m.drawcoastlines()
m.drawcountries()
# draw parallels
parallels = np.arange(-90.,90,10.)
m.drawparallels(parallels)
# draw meridians
meridians = np.arange(180.,360.,10.)
m.drawmeridians(meridians)
ny = data.shape[0]
nx = data.shape[1]
lons, lats = m.makegrid(nx, ny) # get lat/lons of ny by nx evenly space grid
x, y = m(lons, lats) # compute map projection coordinates
# draw filled contours.
clevs = np.linspace(0,70,num=281)
cs = m.contourf(x,y,data,clevs,cmap=plt.cm.jet)
# colorbar
cbar = m.colorbar(cs,location='bottom',pad="5%",ticks=np.linspace(0,70,15))
cbar.set_label('Scale of the data')
plt.title('Some global data', fontsize=14)
Use np.meshgrid() to create the meshgrid of lon-lat, then, convert it to projection coordinates, and the data are ready to generate contours and plot.
Here is the working code:
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
# data for z (2D array)
h2o_north = np.linspace(1, 65, num=90)
h2o_south = np.flipud(h2o_north)
h2o = np.append(h2o_north, h2o_south)
data = np.transpose(np.tile(h2o, (len(h2o_north), 1)))
# create figure and axes instances
fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot()
# create basemap instance
m = Basemap(projection='ortho', lon_0=-50, lat_0=50, resolution='c', ax=ax)
# create meshgrid covering the whole globe with ...
# conforming dimensions of the `data`
lat = np.linspace(-90, 90, data.shape[0])
lon = np.linspace(-180, 180, data.shape[1])
xs, ys = np.meshgrid(lon, lat) # basic mesh in lon, lat (degrees)
x, y = m(xs, ys) # convert (lon,lat) to map (x,y)
# draw filled contours
clevs = np.linspace(0, np.max(data), 60)
cs = m.contourf(x, y, data, clevs, cmap=plt.cm.jet)
m.drawcoastlines()
m.drawcountries()
m.drawmeridians(range(-180, 180, 30))
m.drawparallels(range(-90, 90, 30))
# draw colorbar
cbar = m.colorbar(cs, location='bottom', pad="5%", ticks=np.linspace(0, np.max(data), 5))
cbar.set_label('Scale of the data')
plt.show()
The resulting plot:
Related
Removing Edge colors in Cartopy heatmap
I am attempting to plot a heatmap showing the density of lightning using python's cartopy and matplotlib libraries. I have roughly followed the code here Cartopy Heatmap over OpenStreetMap Background . However, my plot shown below contains solid lines around each transparent bin, which is my problem. The other plot is the same code with random numbers. An ideal solution would be to not display the lines at all, or for the lines to match the bin's face color with the correct transparency. I've done a fair amount of trial and error to remove them in addition to reading some matplotlib documentation. According to the 2d-histogram docs , I should be plotting a QuadMesh object. You should be able to set the linewidth to 0, or have the edgecolor set to none in the QuadMesh. In my code below, I tried doing that yet the lines still persist. I've also tried the pcolormesh as well with the same result. Here is my code. import matplotlib.pyplot as plt import cartopy.crs as ccrs import cartopy.io.shapereader as shpreader import cartopy.feature as cfeature import cartopy.io.img_tiles as cimgt import numpy as np import random #xsize and ysize are integers and lons, lats are 1d numpy arrays of longitude and latitude. def testDensity(xsize, ysize, lons, lats): #Some code below follows example #https://stackoverflow.com/questions/50611018/cartopy-heatmap-over-openstreetmap-background request = cimgt.OSM() fig, ax = plt.subplots(figsize=(xsize,ysize),subplot_kw=dict(projection=request.crs), dpi=200) extent = [-126,-118,41,44] ax.set_extent(extent) ax.add_image(request,8) xynps = ax.projection.transform_points(ccrs.Geodetic(), lons, lats)# print(xynps, type(xynps)) #Create 2-d histogram histogram = ax.hist2d( xynps[:,0] , xynps[:,1] ,cmap='jet', bins=100, zorder=1,alpha=0.5,edgecolors="none",linewidth=0 ) print(histogram[3], dir(histogram[3]) ) histogram[3].set_linewidth(0.0) histogram[3].set_edgecolor("none") #histogram:(frequency, xedges, yedges, image QuadMesh) #ax.pcolormesh(histogram[1], histogram[2], histogram[0], cmap = 'jet', alpha=0.5,edgecolors="none") cbar = plt.colorbar(mappable=histogram[3], ax=ax , shrink=0.5, format='%.1f1' ) cbar.solids.set_rasterized("True")#Removes lines from colorbar cbar.solids.set_edgecolor("face") plt.savefig("densityTest.png", bbox_inches="tight") #Generate random dataset for i in range(0,800): lon = random.randrange(41,44) + random.random() lat = random.randrange(-126,-118) + random.random() lons.append(lon) lats.append(lat) lons = np.array(lons) lats = np.array(lats) testDensity(9,34, lons, lats)
I can't reproduce the problematic results you showed us because of errors in your code. But once I correct the errors in the code and run. I get a good result as shown below. The modified code: def testDensity(xsize, ysize, lons, lats): # Some code below follows example # https://stackoverflow.com/questions/50611018/cartopy-heatmap-over-openstreetmap-background (That's my another answer) request = cimgt.OSM() fig, ax = plt.subplots(figsize=(xsize,ysize),subplot_kw=dict(projection=request.crs), dpi=200) extent = [-126, -118, 41, 44] ax.set_extent(extent) ax.add_image(request, 8) xynps = ax.projection.transform_points(ccrs.Geodetic(), lons, lats) #Create 2-d histogram # histogram = ax.hist2d(xynps[:,0],xynps[:,1],cmap='jet',bins=100,zorder=1,alpha=0.5,edgecolors="none",linewidth=0) #This produces the same result, but shorter. histogram = ax.hist2d( xynps[:,0], xynps[:,1], cmap='jet', bins=100, zorder=1, alpha=0.5) # (Why use these code?) #histogram[3].set_linewidth(0.0) #histogram[3].set_edgecolor("none") #ax.pcolormesh(histogram[1], histogram[2], histogram[0], cmap = 'jet', alpha=0.5,edgecolors="none") # cbar = plt.colorbar(mappable=histogram[3], ax=ax , shrink=0.5, format='%.1f' ) # cbar.solids.set_rasterized("True")#Removes lines from colorbar # cbar.solids.set_edgecolor("face") # ... when this produces good result. cbar = plt.colorbar(histogram[3], ax=ax, pad=0.03, aspect=28, shrink=0.26, format='%.1f') # h[3]: image plt.savefig("densityTest.png", bbox_inches="tight") plt.show() #Generate random dataset lons = [] lats = [] for i in range(0,800): lat = random.randrange(41,44) + random.random() lon = random.randrange(-126,-118) + random.random() lons.append(lon) lats.append(lat) lons = np.array(lons) lats = np.array(lats) #testDensity(9,34, lons, lats) testDensity(10,16, lons, lats) The output plot:
Axis labels for LambertConformal in cartopy at wrong location
I want to plot some data in a LambertConformal projection and add labels to the axes. See the example code below. However, now the x-labels show up twice, and both times in the middle of the plot, instead of at its bottom. When instead I set gl.xlabels_bottom = False and gl.xlabels_top = True, no x-labels are plotted at all. With the y-labels, I do not get this problem; they are just nicely plotted either along the left or right boundary of the plot. How can I get the x-labels at the right location (at the bottom of the figure)? import numpy as np import matplotlib.pyplot as plt import cartopy.crs as ccrs bounds_lon = [-45,-25] bounds_lat = [55,65] lon = np.arange(bounds_lon[0],bounds_lon[1]+0.1,0.1) lat = np.arange(bounds_lat[0],bounds_lat[1]+0.1,0.1) Lon, Lat = np.meshgrid(lon,lat) data = np.ones(np.shape(Lon)) data_crs = ccrs.PlateCarree() projection = ccrs.LambertConformal(central_longitude=np.mean(bounds_lon),central_latitude=np.mean(bounds_lat),cutoff=bounds_lat[0]) plt.figure(figsize=(4,4)) ax = plt.axes(projection=projection) ax.coastlines() ax.contourf(Lon, Lat, data, transform=data_crs) gl = ax.gridlines(crs=ccrs.PlateCarree(), linewidth=2, color='gray', alpha=0.5, linestyle='--') gl.xlabels_bottom = True
Manual repositioning of tick-labels are needed. To do that successfully, requires some other adjustments of the plot settings. Here is the code you can try. import numpy as np import matplotlib.pyplot as plt import cartopy.crs as ccrs bounds_lon = [-45,-25] bounds_lat = [55,65] # make-up data to plot on the map inc = 0.5 lon = np.arange(bounds_lon[0],bounds_lon[1]+inc, inc) lat = np.arange(bounds_lat[0],bounds_lat[1]+inc, inc) Lon, Lat = np.meshgrid(lon,lat) #data = np.ones(np.shape(Lon)) # original `boring` data data = np.sin(Lon)+np.cos(Lat) # better data to use instead data_crs = ccrs.PlateCarree() projection = ccrs.LambertConformal(central_longitude=np.mean(bounds_lon), \ central_latitude=np.mean(bounds_lat), \ #cutoff=bounds_lat[0] ) # Note: `cutoff` causes horizontal cut at lower edge # init plot figure plt.figure(figsize=(15,9)) ax = plt.axes(projection=projection) ax.coastlines(lw=0.2) ax.contourf(Lon, Lat, data, transform=data_crs, alpha=0.5) # set gridlines specs gl = ax.gridlines(crs=ccrs.PlateCarree(), linewidth=2, color='gray', alpha=0.5, linestyle='--') gl.top_labels=True gl.bottom_labels=True gl.left_labels=True gl.right_labels=True plt.draw() #enable access to lables' positions xs_ys = ax.get_extent() #(x0,x1, y0,y1) #dx = xs_ys[1]-xs_ys[0] dy = xs_ys[3]-xs_ys[2] # The extent of `ax` must be adjusted # Extents' below and above are increased new_ext = [xs_ys[0], xs_ys[1], xs_ys[2]-dy/15., xs_ys[3]+dy/12.] ax.set_extent(new_ext, crs=projection) # find locations of the labels and reposition them as needed xs, ys = [], [] for ix,ea in enumerate(gl.label_artists): xy = ea[2].get_position() xs.append(xy[0]) ys.append(xy[1]) # Targeted labels to manipulate has "W" in them if "W" in ea[2].get_text(): x_y = ea[2].get_position() # to check which are above/below mid latitude of the plot # use 60 (valid only this special case) if x_y[1]<60: # labels at lower latitudes curpos = ea[2].get_position() newpos = (curpos[0], 54.7) # <- from inspection: 54.7 ea[2].set_position(newpos) else: curpos = ea[2].get_position() newpos = (curpos[0], 65.3) # <- from inspection: 65.3 ea[2].set_position(newpos) plt.show() Edit1 If you want to move all the lat/long labels to the outside edges, try this code. It is much more concise than the above. import numpy as np import matplotlib.pyplot as plt import cartopy.crs as ccrs bounds_lon = [-45,-25] bounds_lat = [55,65] inc = 0.5 lon = np.arange(bounds_lon[0],bounds_lon[1]+inc, inc) lat = np.arange(bounds_lat[0],bounds_lat[1]+inc, inc) Lon, Lat = np.meshgrid(lon,lat) #data = np.ones(np.shape(Lon)) # boring data data = np.sin(Lon)+np.cos(Lat) # more interesting data_crs = ccrs.PlateCarree() projection = ccrs.LambertConformal(central_longitude=np.mean(bounds_lon), \ central_latitude=np.mean(bounds_lat), \ cutoff=bounds_lat[0] ) # init plot plt.figure(figsize=(15,9)) ax = plt.axes(projection=projection) ax.coastlines(lw=0.2) ax.contourf(Lon, Lat, data, transform=data_crs, alpha=0.3) gl = ax.gridlines(draw_labels=True, x_inline=False, y_inline=False, color='k', linestyle='dashed', linewidth=0.5) gl.top_labels=True gl.bottom_labels=True gl.left_labels=True gl.right_labels=True plt.show() If you want to get bottom edge as a straight line, you can achieve that by dropping the option cutoff=bounds_lat[0] from this line of code:- projection = ccrs.LambertConformal(central_longitude=np.mean(bounds_lon), \ central_latitude=np.mean(bounds_lat), \ cutoff=bounds_lat[0] ) so that it becomes projection = ccrs.LambertConformal(central_longitude=np.mean(bounds_lon), central_latitude=np.mean(bounds_lat)) and you will get the plot like this:-
Griddata and Contourf produce artifacts with increasing steps/levels
I am using SciPy Griddata to interpolate data in its Cartesian form and then plot these data using contourf with a polar projection. When the Cartesian interpolated data is plotted with contourf there are no artifacts. However, when the projection is polar, artifacts develop with increasing "levels". The artifacts are polygons or rays that form near regions of steep gradients. The code below plots the brightness of the sky with the moon. With graphlevels of "12" there isn't an issue. Artifacts develop with graphlevel of "25." My desired level is 80 or more - which shows terrible artifacts. The below is example real data from one night. These artifacts always occur. See images with Levels = 12 and Levels = 80 import numpy as np import matplotlib.pyplot as plt from scipy.interpolate import griddata gridsize =150 graphlevels =12 plt.figure(figsize=(12,10)) ax = plt.subplot(111,projection='polar') x = [72.90,68.00,59.14,44.38,29.63,63.94,59.68,51.92,38.98,26.03,47.34,44.20,38.46,28.89,19.31,23.40,20.40,15.34,10.28,-0.18,-0.14,-0.09,-0.04,0.02,-25.39,-23.66,-20.57,-15.40,-10.23,-47.56,-44.34,-38.54,-28.89,-19.22,-64.01,-59.68,-51.89,-38.90,-25.90,-72.77,-67.84,-58.98,-44.21,-29.44,-72.75,-67.83,-58.96,-44.18,-29.41,-59.63,-51.82,-38.83,-25.84,-47.42,-44.20,-38.40,-28.76,-19.12,-23.40,-20.32,-15.19,-10.08,0.27,0.25,0.23,0.20,23.92,20.80,15.63,10.46,47.93,44.67,38.86,29.17,19.48,64.40,60.03,52.20,39.18,26.15,73.08,68.12,59.26,44.47,29.68,-4.81] y = [12.93,12.01,10.38,7.67,4.99,37.03,34.49,29.93,22.33,14.77,56.60,52.75,45.82,34.26,22.72,64.60,56.14,42.02,27.90,73.66,68.67,59.68,44.68,29.68,69.12,64.45,56.00,41.92,27.84,56.26,52.45,45.56,34.08,22.61,36.59,34.11,29.61,22.11,14.62,12.48,11.62,10.04,7.43,4.83,-13.33,-12.31,-10.78,-8.21,-5.58,-34.84,-30.36,-22.87,-15.36,-57.04,-53.20,-46.31,-34.83,-23.34,-65.20,-56.72,-42.62,-28.53,-69.33,-60.31,-45.31,-30.31,-65.09,-56.63,-42.55,-28.47,-56.81,-52.99,-46.13,-34.69,-23.23,-36.99,-34.53,-30.08,-22.66,-15.22,-12.73,-11.93,-10.44,-7.94,-5.40,-1.22,] skybrightness = [19.26,19.31,19.21,19.65,19.40,19.26,19.23,19.43,19.57,19.52,19.19,19.31,19.33,19.68,19.50,19.29,19.45,19.50,19.23,18.98,19.28,19.46,19.54,19.22,19.03,19.18,19.35,19.37,19.08,18.99,18.98,19.26,19.36,19.08,18.79,18.85,19.13,19.17,19.05,18.51,18.64,18.88,18.92,18.93,18.12,18.34,18.72,18.82,18.74,18.22,18.46,18.76,18.26,18.13,18.24,18.46,18.58,17.30,18.38,18.08,18.24,17.68,18.34,18.46,18.65,18.23,18.70,18.52,18.79,18.83,18.18,18.51,19.01,19.08,19.08,18.99,19.02,19.07,19.20,19.27,19.06,19.01,19.28,19.46,19.30,18.94] xgrid = np.linspace(min(x), max(x),gridsize) ygrid = np.linspace(min(y), max(y),gridsize) xgrid, ygrid = np.meshgrid(xgrid, ygrid, indexing='ij') nsb_grid = griddata((x,y),skybrightness,(xgrid, ygrid), method='linear') r = np.sqrt(xgrid**2 + ygrid**2) theta = np.arctan2(ygrid, xgrid) plt.rc('ytick', labelsize=16) ax.set_facecolor('#eeddcc') colors = plt.cm.get_cmap('RdYlBu') levels,steps = np.linspace(min(skybrightness), max(skybrightness)+0.3,graphlevels, retstep=True) ticks = np.linspace(min(skybrightness), max(skybrightness)+0.3,12) cax = ax.contourf(theta, r, nsb_grid, levels=levels, cmap=colors) cbar = plt.colorbar(cax, fraction=0.046, pad=0.04, ticks=ticks) cbar.set_label(r'mag/arcsec$^2$') ax.set_theta_zero_location('N') ax.set_theta_direction(-1) ax.set_rmax(75) ax.set_yticks(range(10, 80, 20)) ax.set_xticklabels([r'N', r'NE', r'E', r'SE', r'S', r'SW', r'W', r'NW']) ax.grid(alpha=0.3) plt.savefig('StackOverflowHELP.png')
I am going to leave my question and this answer on StackOverflow... because I did get an answer from the developers of Matploblib. The problem is Contourf . In its attempt to project data in polar dimensions there are overlaps and extensions of polygons at the cyclic boundaries that cause problems. The only way to avoid this is to add points at the boundary. To quote the developer: The workaround is a lot of effort and has to be tuned to each particular problem, so is a very long way from being ideal. We (Matplotlib) should do better in these situations. Inserting extra points into the triangulation isn't the right approach, we should instead correct the lines/polygons that traverse the discontinuity to provide a general solution. See https://github.com/matplotlib/matplotlib/issues/20060 for the full discussion The answer I settled on is to interpolate and render the result in Cartesian space. Then I format an empty polar plot with axes and labels to overlay on the top... and get on with my life! import numpy as np import matplotlib.pyplot as plt from scipy.interpolate import griddata gridsize =150 graphlevels = 200 fig = plt.figure(figsize=(12,10)) ax = fig.add_subplot(111, aspect='equal') pax = fig.add_subplot(111,projection='polar') pax.set_facecolor('none') ax.set_axis_off() ax.set_xlim([-75,75]) ax.set_ylim([-75,75]) x = [72.90,68.00,59.14,44.38,29.63,63.94,59.68,51.92,38.98,26.03,47.34,44.20,38.46,28.89,19.31,23.40,20.40,15.34,10.28,-0.18,-0.14,-0.09,-0.04,0.02,-25.39,-23.66,-20.57,-15.40,-10.23,-47.56,-44.34,-38.54,-28.89,-19.22,-64.01,-59.68,-51.89,-38.90,-25.90,-72.77,-67.84,-58.98,-44.21,-29.44,-72.75,-67.83,-58.96,-44.18,-29.41,-59.63,-51.82,-38.83,-25.84,-47.42,-44.20,-38.40,-28.76,-19.12,-23.40,-20.32,-15.19,-10.08,0.27,0.25,0.23,0.20,23.92,20.80,15.63,10.46,47.93,44.67,38.86,29.17,19.48,64.40,60.03,52.20,39.18,26.15,73.08,68.12,59.26,44.47,29.68,-4.81] y = [12.93,12.01,10.38,7.67,4.99,37.03,34.49,29.93,22.33,14.77,56.60,52.75,45.82,34.26,22.72,64.60,56.14,42.02,27.90,73.66,68.67,59.68,44.68,29.68,69.12,64.45,56.00,41.92,27.84,56.26,52.45,45.56,34.08,22.61,36.59,34.11,29.61,22.11,14.62,12.48,11.62,10.04,7.43,4.83,-13.33,-12.31,-10.78,-8.21,-5.58,-34.84,-30.36,-22.87,-15.36,-57.04,-53.20,-46.31,-34.83,-23.34,-65.20,-56.72,-42.62,-28.53,-69.33,-60.31,-45.31,-30.31,-65.09,-56.63,-42.55,-28.47,-56.81,-52.99,-46.13,-34.69,-23.23,-36.99,-34.53,-30.08,-22.66,-15.22,-12.73,-11.93,-10.44,-7.94,-5.40,-1.22,] skybrightness = [19.26,19.31,19.21,19.65,19.40,19.26,19.23,19.43,19.57,19.52,19.19,19.31,19.33,19.68,19.50,19.29,19.45,19.50,19.23,18.98,19.28,19.46,19.54,19.22,19.03,19.18,19.35,19.37,19.08,18.99,18.98,19.26,19.36,19.08,18.79,18.85,19.13,19.17,19.05,18.51,18.64,18.88,18.92,18.93,18.12,18.34,18.72,18.82,18.74,18.22,18.46,18.76,18.26,18.13,18.24,18.46,18.58,17.30,18.38,18.08,18.24,17.68,18.34,18.46,18.65,18.23,18.70,18.52,18.79,18.83,18.18,18.51,19.01,19.08,19.08,18.99,19.02,19.07,19.20,19.27,19.06,19.01,19.28,19.46,19.30,18.94] xgrid = np.linspace(min(x), max(x),gridsize) ygrid = np.linspace(min(y), max(y),gridsize) xgrid, ygrid = np.meshgrid(xgrid, ygrid, indexing='ij') nsb_grid = griddata((x,y),skybrightness,(xgrid, ygrid), method='linear') plt.rc('ytick', labelsize=16) #colorbar font colors = plt.cm.get_cmap('RdYlBu') levels,steps = np.linspace(min(skybrightness), max(skybrightness)+0.3,graphlevels, retstep=True) ticks = np.linspace(min(skybrightness), max(skybrightness)+0.3,12) cax = ax.contourf(xgrid, ygrid, nsb_grid, levels=levels, cmap=colors) cbar = plt.colorbar(cax, fraction=0.046, pad=0.04, ticks=ticks) cbar.set_label(r'mag/arcsec$^2$') pax.set_theta_zero_location('N') pax.set_theta_direction(-1) pax.set_rmax(75) pax.set_yticks(range(10, 80, 20)) pax.set_xticklabels([r'N', r'NE', r'E', r'SE', r'S', r'SW', r'W', r'NW']) pax.grid(alpha=0.3)
Overwrite points from pcolormesh if they aren't contained in a polygon
I'm trying to plot a map whereby a spatial pattern is plotted over the land using pcolormesh or contourf. A shapefile/polygon that describes the border of the UK is then overlayed onto this plot. My problem is how to directly access the points that fall outside the polygon to set them as 0 or directly colour them a single colour e.g. white. See the following minimal working example import geopandas as gpd import matplotlib.pyplot as plt import numpy as np # Load polygon world = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres")) UK = world[world.iso_a3 == "GBR"] UK.boundary.plot() # Simulate a spatial pattern xlims = (-8, 3) ylims = (49, 60) resolution = 0.05 y, x = np.mgrid[slice(ylims[0], ylims[1] + resolution, resolution), slice(xlims[0], xlims[1] + resolution, resolution)] z = 0.5*x+np.sin(x)**2+ np.cos(y) # Plot fig, ax=plt.subplots(figsize=(6, 10)) im = ax.pcolormesh(x, y, z, cmap='viridis') fig.colorbar(im, ax=ax) UK.boundary.plot(ax=ax, color='black') I have tried excluding any points in the original dataset and then generating the pcolormesh. However, pcolormesh interpolates between points. This results in a series of points being generated from Northern Ireland down to Cornwall. Just to be clear, what I would like is to fill outside the polygon. Thanks for any help.
Rather than what you request (modifying the values of z), I plot another layer of pcolormesh() on top to get the desired effect. In this process, a new_z array is created with individual values obtained from point-within_polygon operation. A custom colormap, new_binary is created to use with new_z to plot this layer and get the final plot. import geopandas as gpd import matplotlib.pyplot as plt import numpy as np import matplotlib as mpl from shapely.geometry import Point # Load polygon world = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres")) UK = world[world.iso_a3 == "GBR"] # plot 1 #UK.boundary.plot() # Simulate a spatial pattern xlims = (-8, 3) ylims = (49, 60) resolution = 0.05 # 0.05 # slice() y, x = np.mgrid[slice(ylims[0], ylims[1] + resolution, resolution), slice(xlims[0], xlims[1] + resolution, resolution)] z = 0.5*x+np.sin(x)**2+ np.cos(y) # Target geometry, for point inside/outside polygon operation ukgeom = UK['geometry'].values[0] def prep_z(Xs,Ys,Zs): # Xs,Ys: result of np.meshgrid(lon, lat) # Zs: function of(Xs,Ys) to be manipulated; here hard-coded as `new_z` for ro,(arow,acol) in enumerate(zip(Xs,Ys)): # need 2 level loop to handle each grid point for co,xiyi in enumerate(zip(arow,acol)): pnt1 = Point(xiyi) if pnt1.within(ukgeom): new_z[ro][co]=1 #0:white, 1:black with cm='binary' else: new_z[ro][co]=0 pass pass # Usage of the function above: prep_z(x,y,z) # Result: new_z is modified. # New z for locations in/outside-polygon operation new_z = np.zeros(z.shape) prep_z(x,y,z) # create custom colormap to use later new_binary = mpl.colors.ListedColormap(np.array([[1., 1., 1., 1.], [0., 0., 0., 0.]])) # 0:white, 1:transparent with cm='new_binary' # do not use alpha option to get the intended result # Plot 2 fig, ax = plt.subplots(figsize=(6, 10)) im = ax.pcolormesh(x, y, z, cmap='viridis', zorder=1) im2 = ax.pcolormesh(x, y, new_z, cmap=new_binary, zorder=2) # do not use alpha to get transparent mask UK.boundary.plot(ax=ax, color='black', zorder=10) fig.colorbar(im, ax=ax, shrink=0.5) plt.show()
Problems with pcolormesh in Cartopy
I'm trying to adapt the Cartopy example plot for circular South Polar Stereographic plots to the North Pole and add data to it. I have a couple questions. First, in the example code, the land feature is added before the ocean feature. When I did that, I got a map with only ocean. I reversed the order of the call in the code below and get a map with land and ocean. Why did the other order work with the South Polar example? Second, and more importantly, I can't figure out why my pcolormesh call isn't having any effect. I'm using Python 2.7.7, matplotlib 1.5.1, and Cartopy 0.15.1. import matplotlib.path as mpath import matplotlib.pyplot as plt import numpy as np import cartopy.crs as ccrs import cartopy.feature lats = np.linspace(60,90,30) lons = np.linspace(0,360,200) X,Y = np.meshgrid(lons,lats) Z = np.random.normal(size = X.shape) def main(): fig = plt.figure(figsize=[10, 5]) ax = plt.subplot(1, 1, 1, projection=ccrs.NorthPolarStereo()) fig.subplots_adjust(bottom=0.05, top=0.95, left=0.04, right=0.95, wspace=0.02) # Limit the map to -60 degrees latitude and below. ax.set_extent([-180, 180, 60, 60], ccrs.PlateCarree()) ax.gridlines() ax.add_feature(cartopy.feature.OCEAN) ax.add_feature(cartopy.feature.LAND) # 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) ax.pcolormesh(X,Y,Z,transform=ccrs.PlateCarree()) plt.show() if __name__ == '__main__': main()
Your code leaves cartopy to dictate the order of feature plots on the map, as a result, some features can be hidden with no clues. It is possible to specify the order of plots explicitly. The order of features plot is controlled by zorder, which can be specified with zorder=integer in most plotting statements. Here is a modified code that produces a better plot. # your data lats = np.linspace(60, 90, 30) lons = np.linspace(0, 360, 160) X,Y = np.meshgrid(lons, lats) Z = np.random.normal(size = X.shape) # new data for pcolormesh plot latz = np.linspace(75, 90, 15) lonz = np.linspace(0, 360, 160) X1,Y1 = np.meshgrid(lonz, latz) Z1 = np.random.normal(size = X1.shape) def main(): fig = plt.figure(figsize=[10, 10]) ax = plt.subplot(1, 1, 1, projection=ccrs.NorthPolarStereo()) fig.subplots_adjust(bottom=0.05, top=0.95, left=0.04, right=0.95, wspace=0.02) # Limit the map to -60 degrees latitude and below. ax.set_extent([-180, 180, 60, 60], ccrs.PlateCarree()) ax.gridlines() # zorder can be used to arrange what is on top ax.add_feature(cartopy.feature.LAND, zorder=4) # land is specified to plot above ... ax.add_feature(cartopy.feature.OCEAN, zorder=1) # ... the ocean # 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) # pcolormesh is specified to plot on top of the ocean but below land ax.pcolormesh(X1, Y1, Z1, transform=ccrs.PlateCarree(), zorder=3) plt.show() if __name__ == '__main__': main()