Overlaying contour plot on basemap - python

Please can anyone look at this code it is not giving a desired result.
# use the Miller projection
m = Basemap( projection='mill', resolution='i', lon_0=0., lat_0=0.)
# draw coastline, map-boundary
m.drawcoastlines(linewidth=0.5)
m.drawmapboundary( fill_color='white' )
# draw grid
m.drawparallels( np.arange(-90.,90.,30.), labels=[1,0,0,0] )
m.drawmeridians( np.arange(-180.,180.,60.), labels=[0,0,0,1] )
# Creating 2-D grid of features
[Lat, Lon] = np.meshgrid(lats, lons)
pLat, pLon = m(Lat, Lon)
# plots filled contour plot
cfig = m.contourf(pLon, pLat, OLR_2011, levels= clevel, extend = 'both', cmap="jet")
cbar = m.colorbar(cfig, location = 'right', pad = "10%")
plt.show()
The result is shown in this figure.
I want the overplot to cover the basemap. Please where did I go wrong.

When converting from lat/lon to map coordinates, Basemap takes (Lon, Lat), not (Lat, Lon).
So you need to replace:
pLat, pLon = m(Lat, Lon)
with:
pLon, pLat = m(Lon, Lat)

Related

How to apply topographic hillshading/relief to a filled contour plot in python

I am trying to make a plot of some gridded data, with topographic relief shading based on some underlying elevation data.
Most of the examples I can find in the matplotlib documentation only show to to shade the elevation data itself - not how to shade some separate array based on elevation.
I was able to come up with the following code which almost does what I want it to do. I first create a "dummy" image to generate an RGBA array with, and then pass that RGBA array to the
LightSource.shade_rgb() function.
#load elevation data
dem = get_sample_data('jacksboro_fault_dem.npz', np_load=True)
z = dem['elevation']
#make a grid to plot over elevation
x = np.arange(z.shape[1])
y = np.arange(z.shape[0])
x_grid, y_grid = np.meshgrid(x,y)
data = x_grid + y_grid
#make a dummy plot to extract rgba information from
fig,ax = plt.subplots()
im = ax.imshow(data)
plt.close(fig)
#extract RGBA array from dummy image
rgba = np.reshape(im.to_rgba(data.ravel()), data.shape + (4,))
#apply relief shading
ls = LightSource(azdeg=315, altdeg=45)
data_hillshade = ls.shade_rgb(rgba, z, blend_mode = 'soft', vert_exag = 0.5)
#plot hillshaded data
fig, ax = plt.subplots()
ax.imshow(data_hillshade)
However, what I really want is a filled contour plot (i.e. contourf) with hillshading.
If I try a similar method, but use contourf to create the dummy plot instead of imshow...
#load elevation data
dem = get_sample_data('jacksboro_fault_dem.npz', np_load=True)
z = dem['elevation']
#make a grid to plot over elevation
x = np.arange(z.shape[1])
y = np.arange(z.shape[0])
x_grid, y_grid = np.meshgrid(x,y)
data = x_grid + y_grid
#make a dummy plot to extract rgba information from
fig,ax = plt.subplots()
im = ax.contourf(data)
plt.close(fig)
#extract RGBA array from dummy image
rgba = np.reshape(im.to_rgba(data.ravel()), data.shape + (4,))
#apply relief shading
ls = LightSource(azdeg=315, altdeg=45)
data_hillshade = ls.shade_rgb(rgba, z, blend_mode = 'soft', vert_exag = 0.5)
#plot hillshaded data
fig, ax = plt.subplots()
ax.imshow(data_hillshade)
...I just end up with the same plot as before, with no contour lines. The to_rgba() function I am using to convert the image to an RGBA array apparently does not conserve the "contoury-ness" of the image.
Is there any way to get a hillshaded contour plot with matplotlib? Essentially, I would like to have the image above, but with several distinct contour colors, rather than a smooth gradation of colors.
EDIT:
I came up with a solution below, but I'm leaving this as an open question because it still doesn't do exactly what I want. As can be seen in the example here, using the Discrete bounds normalization with pcolormesh leaves the plot with a "gridded" appearance - it does not quite have the smooth contour lines the contourf gives you.
I was able to solve this by incorporating the BoundaryNorm normalization function to make a pcolormesh plot look like a contour plot.
#load elevation data
dem = get_sample_data('jacksboro_fault_dem.npz', np_load=True)
z = dem['elevation']
#make a grid to plot over elevation
x = np.arange(z.shape[1])
y = np.arange(z.shape[0])
x_grid, y_grid = np.meshgrid(x,y)
data = x_grid + y_grid
bounds = np.linspace(data.min(), data.max(), 10)
norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256)
#make a dummy plot to extract rgba information from
fig,ax = plt.subplots()
im = ax.pcolormesh(data, norm=norm)
plt.close(fig)
#extract RGBA array from dummy image
rgba = np.reshape(im.to_rgba(data.ravel()), data.shape + (4,))
#apply relief shading
ls = LightSource(azdeg=315, altdeg=45)
data_hillshade = ls.shade_rgb(rgba, z, blend_mode = 'soft', vert_exag = 0.5)
#plot hillshaded data
fig, ax = plt.subplots()
ax.imshow(data_hillshade)
EDIT: This is not a perfect solution, because it results in a "gridded"-looking plot, and does not give the smooth contour lines that contourf gives you.
Works perfectly for me thank you so much for updating your solution !
Adding interpolation in the plot makes it smoother, not sure if that's what you want though:
ax.imshow(data_hillshade,interpolation='bilinear')

Cartopy plotting extra points at poles when passed one at a time

When plotting an orthographic projection with some points on the other side of the globe, how come the first approach plots as expected, but the second takes all the points that would be on the other side of the globe and plots them at the pole of the projection? Is there a solution beyond filtering out the points that are out of sight, and if not what is the best way to do that for a pole at an arbitrary lon/lat (as opposed to the north pole, which is relatively trivial)?
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
projection = ccrs.Orthographic(0, 90)
transform = ccrs.Geodetic()
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection = projection)
ax.coastlines()
ax.set_global()
ax.gridlines()
npoints = 100
np.random.seed(71077345)
lon = np.random.sample(npoints) * 360
lat = np.random.sample(npoints) * 180 - 90
plt.plot(lon,
lat,
'ro',
alpha = 0.3,
transform = transform)
for i in range(npoints):
plt.plot(lon[i],
lat[i],
'b.',
alpha = 0.3,
transform = transform)
(Partial answer to the question)
To filter the points on the upper hemisphere, use this code
for i in range(npoints):
if lat[i]>=0:
# this plots points above/on equator
ccode = 'b^'
ax.plot( lon[i], lat[i],
ccode,
alpha = .3,
transform = ccrs.PlateCarree()
)
else:
# this skips points below equator
pass
This bug has been fixed in v0.19 and beyond by #1710.

Aligning data (contourf) on Basemap

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:

drawing polygon onto basemap

There are many similar questions to this (How to draw rectangles on a Basemap , and http://matplotlib.1069221.n5.nabble.com/display-a-filled-lat-lon-basemap-rectangle-td11562.html) but I still cannot figure out how to do this.
I want to shade an area covered by two boxes, and the coordinates of each corner (UR = upper right, LL = lower left..) are given by :
box 1:
UR_box1_lat = 72.9
UR_box1_lon = -160
LL_box1_lat = 71.2
LL_box1_lon = -176.5
box 2 :
UL_box2_lat = LL_box1_lat
UL_box2_lon = LL_box1_lon
LR_box2_lat = 69.304
LR_box2_lon = -164.5
This produces my underlying map of the domain where I want to shade my polygons on top of:
# make map with these (i_total, j_total) indices as a box shaded or outlined..
# read in etopo5 topography/bathymetry.
url = 'http://ferret.pmel.noaa.gov/thredds/dodsC/data/PMEL/etopo5.nc'
etopodata = Dataset(url)
topoin = etopodata.variables['ROSE'][:]
lons = etopodata.variables['ETOPO05_X'][:]
lats = etopodata.variables['ETOPO05_Y'][:]
# shift data so lons go from -180 to 180 instead of 20 to 380.
topoin,lons = shiftgrid(180.,topoin,lons,start=False)
# plot topography/bathymetry as an image.
# create the figure and axes instances.
fig = plt.figure()
ax = fig.add_axes([0.1,0.1,0.8,0.8])
# setup of basemap ('lcc' = lambert conformal conic).
# use major and minor sphere radii from WGS84 ellipsoid.
m = Basemap(llcrnrlon=175.,llcrnrlat=50.,urcrnrlon=-120.,urcrnrlat=75.,\
rsphere=(6378137.00,6356752.3142),\
resolution='l',area_thresh=1000.,projection='lcc',\
lat_1=66.,lon_0=-169.,ax=ax)
# transform to nx x ny regularly spaced 5km native projection grid
nx = int((m.xmax-m.xmin)/5000.)+1; ny = int((m.ymax-m.ymin)/5000.)+1
topodat = m.transform_scalar(topoin,lons,lats,nx,ny)
# plot image over map with imshow.
im = m.imshow(topodat,cm.GMT_haxby)
# draw coastlines and political boundaries.
m.drawcoastlines()
m.drawcountries()
m.drawstates()
# draw parallels and meridians.
# label on left and bottom of map.
parallels = np.arange(0.,80,15.)
m.drawparallels(parallels,labels=[1,0,0,0])
#m.drawparallels(np.array([50.,70]))
meridians = np.arange(10.,360.,30.)
m.drawmeridians(meridians,labels=[1,0,0,1])
# add colorbar
cb = m.colorbar(im,"right", size="5%", pad='2%')
I have tried (but this is only an attempt at the top box, but I want the entire area shaded of both boxes):
def draw_screen_poly( lats, lons, m):
x, y = m( lons, lats )
xy = zip(x,y)
poly = Polygon( xy, facecolor='red', alpha=0.4 )
plt.gca().add_patch(poly)
lats_box1 = np.linspace(71.2, 72.9, num=25)
lons_box1 = np.linspace(-176.5, -160, num=25)
#lats_box1 = [71.2,72.9,72.9,71.2]
#lons_box1 = [-160,-160,-176.5,-176.5]
#m = Basemap(projection='sinu',lon_0=0)
#m.drawcoastlines()
#m.drawmapboundary()
draw_screen_poly( lats_box1, lons_box1, m )
and also (this is also an attempt at just the top box):
x1,y1 = m(71.2,-176.5)
x2,y2 = m(72.9,-176.5)
x3,y3 = m(72.9,-160)
x4,y4 = m(71.2,-160)
p = Polygon([(x1,y1),(x2,y2),(x3,y3),(x4,y4)],facecolor='red',edgecolor='blue',linewidth=2)
plt.gca().add_patch(p)
plt.show()
Any help is greatly appreciated, I have been stuck on this for quite some time

Python Basemap does not show plotted points

I just started using Python basemap, but I can not make point to appear in my map!... Here is the function I'm trying to build:
def map_plot(df):
df = df.apply(pd.to_numeric, errors='coerce').dropna()
m = Basemap(projection='mill',
llcrnrlat=25,
llcrnrlon=-130,
urcrnrlat=50,
urcrnrlon=-60,
resolution='l') #proyeccion de Miller
m.drawcoastlines()
m.drawcountries(linewidth=2)
m.drawstates(color='b')
m.fillcontinents(color = '#888888')
x_map, y_map = m(df['Latitude'].values, df['Longitud'].values)
x = []
y = []
for x_map, y_map in zip(x_map, y_map):
if y_map > 0: continue
x.append(x_map)
y.append(y_map)
m.plot(x, y, 'g^', markersize=5)
plt.show()
So, the map shows, but not a single point is plotted.
Here is how my data looks before calculating the projection coordinates:
,Latitude,Longitud
0,35.93,-77.79
1,35.93,-77.79
2,38.78,-80.22
3,37.65,-82.25
4,41.12,-104.82
5,41.85,-80.83
6,39.7,-84.21
7,39.9,-80.94
8,39.1,-84.54
9,39.93,-83.82
10,40.05,-82.39
What am I doing wrong?
Thank you!!!
You need grid coordinates (x, y) to plot points on the map. Here is the code that implements the required coordinate transformation and does the plotting.
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
m = Basemap(projection='mill',
llcrnrlat=20,
llcrnrlon=-130,
urcrnrlat=50,
urcrnrlon=-60,
resolution='l') # Miller proj, USA
m.drawcoastlines()
m.drawcountries(linewidth=2)
m.drawstates(color='b')
m.fillcontinents(color = '#888888')
# sample data to plot with
lons = [-100, -75] # degrees
lats = [25, 40]
# plotting points
for lon, lat in zip(lons, lats):
x, y = m.projtran(lon, lat) # coord transformation
m.plot(x, y, 'r^', markersize=15) # needs grid coords to plot
plt.show()
Your line
if y_map > 0: continue
is causing you problems. Every value of y is >0, so the continue is being applied, which skips to the next iteration of the for loop. Consequently, your lines
x.append(x_map)
y.append(y_map)
are never used

Categories

Resources