Tilted grid network plotting in Basemap - python

Here is an example figure as illustration.
This plot present the satellite SO2 column data for part of Europe.
Due to the difference between satellite and longitude, the grid network which fit the satellite scanning principle are not parallel to longitude.
I don't know if it's possible to draw this kind of grid network using pcolor or pcolormesh in matplotlib.basemap. So, I post my question here.

I stumbled upon this question because I was also looking for a way to plot gridded satellite measurements on a map, using matplotlib and basemap.
I am not sure if my idea is relevant to your question, as my pixels can assume only a very limited discrete number of values (4), but I decided to answer anyway, also to find out if you eventually found an efficient solution. What I did was to directly plot each single pixel as a polygon on the map, by using the method Polygon.
I set the alpha value to be function of the underlying physical measurement. In my case—a cloud mask plot—this strategy works out pretty well.
Here's the function that gets called for each pixel to be plotted:
def draw_cloud_pixel(lats, lons, index, mapplot):
"""Draw a pixel on the map. The fill color alpha level depends on the cloud index,
ranging from 0.1 (almost fully transparent) for confidently clear pixels to 1 (fully opaque)
for confidently cloudy pixels.
Keyword arguments:
lats -- Array of latitude values for the pixel 4 corner points (numpy array)
lons -- Array of longitudes values for the pixel 4 corner points (numpy array)
index -- Cloud mask index for given pixel:
0: confidently_cloudy
1: probably_cloudy
2: probably_clear
3: confidently_clear
mapplot -- Map object for coordinate transformation
Returns:
None
"""
x, y = mapplot(lons, lats)
xy = zip(x,y)
poly = Polygon(xy, facecolor='white', alpha=1-0.3*index)
plt.gca().add_patch(poly)
In my main plotting routine, I then call the draw_cloud_pixel function for each pixel in the selected region:
# draw plot, each pixel at the time
for scanline in xrange(select_cp_lat.shape[0]):
for pixel in xrange(select_cp_lat.shape[1]):
draw_cloud_pixel(select_cp_lat[scanline, pixel,:],
select_cp_lon[scanline, pixel,:],
cloud_mask[scanline, pixel],
mapplot)
I get plots like this one:

Look on different examples from this page: http://www.uvm.edu/~jbagrow/dsv/heatmap_basemap.html
Main idea of a sample is plot a pcolormesh on a basemap:
import csv
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
# load earthquake epicenters:
# http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/1.0_week.csv
lats, lons = [], []
with open('earthquake_data.csv') as f:
reader = csv.reader(f)
next(reader) # Ignore the header row.
for row in reader:
lat = float(row[1])
lon = float(row[2])
# filter lat,lons to (approximate) map view:
if -130 <= lon <= -100 and 25 <= lat <= 55:
lats.append( lat )
lons.append( lon )
# Use orthographic projection centered on California with corners
# defined by number of meters from center position:
m = Basemap(projection='ortho',lon_0=-119,lat_0=37,resolution='l',\
llcrnrx=-1000*1000,llcrnry=-1000*1000,
urcrnrx=+1150*1000,urcrnry=+1700*1000)
m.drawcoastlines()
m.drawcountries()
m.drawstates()
# ######################################################################
# bin the epicenters (adapted from
# http://stackoverflow.com/questions/11507575/basemap-and-density-plots)
# compute appropriate bins to chop up the data:
db = 1 # bin padding
lon_bins = np.linspace(min(lons)-db, max(lons)+db, 10+1) # 10 bins
lat_bins = np.linspace(min(lats)-db, max(lats)+db, 13+1) # 13 bins
density, _, _ = np.histogram2d(lats, lons, [lat_bins, lon_bins])
# Turn the lon/lat of the bins into 2 dimensional arrays ready
# for conversion into projected coordinates
lon_bins_2d, lat_bins_2d = np.meshgrid(lon_bins, lat_bins)
# convert the bin mesh to map coordinates:
xs, ys = m(lon_bins_2d, lat_bins_2d) # will be plotted using pcolormesh
# ######################################################################
# define custom colormap, white -> nicered, #E6072A = RGB(0.9,0.03,0.16)
cdict = {'red': ( (0.0, 1.0, 1.0),
(1.0, 0.9, 1.0) ),
'green':( (0.0, 1.0, 1.0),
(1.0, 0.03, 0.0) ),
'blue': ( (0.0, 1.0, 1.0),
(1.0, 0.16, 0.0) ) }
custom_map = LinearSegmentedColormap('custom_map', cdict)
plt.register_cmap(cmap=custom_map)
# add histogram squares and a corresponding colorbar to the map:
plt.pcolormesh(xs, ys, density, cmap="custom_map")
cbar = plt.colorbar(orientation='horizontal', shrink=0.625, aspect=20, fraction=0.2,pad=0.02)
cbar.set_label('Number of earthquakes',size=18)
#plt.clim([0,100])
# translucent blue scatter plot of epicenters above histogram:
x,y = m(lons, lats)
m.plot(x, y, 'o', markersize=5,zorder=6, markerfacecolor='#424FA4',markeredgecolor="none", alpha=0.33)
# http://matplotlib.org/basemap/api/basemap_api.html#mpl_toolkits.basemap.Basemap.drawmapscale
m.drawmapscale(-119-6, 37-7.2, -119-6, 37-7.2, 500, barstyle='fancy', yoffset=20000)
# make image bigger:
plt.gcf().set_size_inches(15,15)
plt.show()

Related

matplotlib.patches.Arc and using the clip_path parameter

I want to use in matplotlib.patches.Arc the clip_path parameter, but do not succeed.
Next is just an example, where I want to see not the complete orange arc but only the partial orange arc between y-axis and the red circle by using the clip_path parameter, but do no understand how to define the clip_path parameters. Thanks.
import math as m
import matplotlib.pyplot as plt
import matplotlib.patches as pat
plt.figure(figsize=(10,10),dpi=300)
ag=10
plt.axis([-ag,ag,-ag,ag])
plt.grid(True)
circle1 = plt.Circle((0, 2.5), 7, color='r',fill=False)
plt.gcf().gca().add_artist(circle1)
myarc=pat.Arc((0,0),25,18,angle=0,theta1=0,theta2=355,color="orange")
plt.gcf().gca().add_artist(myarc)
plt.savefig("myarc.png")
plt.show()
This is what I got:
Just a further remark: With next modification of theta1 and theta2 angle I get what I need, but for this the two intersections need to be determined first. My intention is to avoid these calculations and just draw an ellipse and defining two clipping paths (the red circle and the y-axis).
myarc=pat.Arc((0,0),25,18,angle=0,theta1=110,theta2=152,color="orange")
To clip the arc by the circle, you can use myarc.set_clip_path(circle1). It is important that both the arc and the circle are previously added to the plot (ax.add_artist()). Note that clipping by the borders of the axes happens automatically.
To create more complicated clipping, the shapely is probably handier.
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
fig, ax = plt.subplots(figsize=(10, 6))
circle1 = plt.Circle((0, 2.5), 7, color='r', fill=False)
ax.add_artist(circle1)
myarc = mpatches.Arc((0, 0), 25, 18, angle=0, theta1=0, theta2=355, color="orange", lw=5)
ax.add_artist(myarc)
myarc.set_clip_path(circle1)
ag = 10
ax.set_xlim(-ag, ag)
ax.set_ylim(-ag, ag)
plt.grid(True)
ax.set_aspect('equal') # set the aspect ratio so circles look like circles
plt.show()
By using the steps of answer (1) I got the wanted result without the need to calculate all the intersections. Steps:
Defining and Plotting series of curves
Defining clipping areas by using clip_path option (e.g. circles or shaping an area by concatenating 1D-arrays through mathematical function results)
Using clip_path to get rid of unwanted portion of curves
# Import python Modules
import math as m
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Polygon
## constants for the circles
Loc=37 # in degree
StepAl=3 # in degree
StepAz=10 # in degree
rAequ=6.3 # Radius
rWkSt=9.6 # Radius
Ze=3.14 # Distance
## red AlCircles, in total 31
AlCircle=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
i=0
while i<=30:
AlCircle[i]=[0,0,0,0]
i=i+1
# Calculating the parameters of the AlCircles
i=0
while i<=30:
AlCircle[i][0]=rAequ*m.tan((-Loc+i*StepAl)/2/180*m.pi) # lower y-Value
AlCircle[i][1]=rAequ*m.tan((-Loc+180-i*StepAl)/2/180*m.pi) # upper y-Value
AlCircle[i][2]=(AlCircle[i][1]-AlCircle[i][0])/2 # Radius
AlCircle[i][3]=AlCircle[i][0]+AlCircle[i][2] # Center
i=i+1
## green AzCircles, in total 18
DZ=rAequ/m.cos(Loc/180*m.pi) # Distance
AzCircle=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
i=0
while i<=17:
AzCircle[i]=[0,0]
i=i+1
# Calculating the parameters of the AzCircles
i=1
while i<=17:
AzCircle[i][0]=DZ*m.tan((-90+i*StepAz)/180*m.pi) # distance Center to y-Axis
AzCircle[i][1]=rAequ/m.cos(Loc/180*m.pi)/m.cos((-90+i*StepAz)/180*m.pi) # Radius of AzCircles
i=i+1
### Generating Plots
plt.figure(figsize=(10,10),dpi=100)
ag=rWkSt
plt.axis([-ag,ag,-ag,ag])
plt.grid(True)
# Plotting blue Circle
circle0=plt.Circle((0,0),rWkSt,color='b',fill=False)
plt.gcf().gca().add_artist(circle0)
# Plotting red AlCircles
i=0
while i<=30:
# defining Cliparea1
myCliparea1=plt.Circle((0,0),rWkSt,color="b",ls="dotted",fill=False)
plt.gcf().gca().add_artist(myCliparea1)
# calculating AlCircles and plotting
circle1=plt.Circle((0,AlCircle[i][3]),AlCircle[i][2],color='r',fill=False)
plt.gcf().gca().add_artist(circle1)
circle1.set_clip_path(myCliparea1) # performing clipping
i=i+1
# Plotting green AzCircles
i=1
while i<=17: # nur bis 17
xA=9.072582 # precalculated Intersection for f1(x) and f2(x)
# f1(x) for lower clipping area border line
x1=np.arange(-xA,+xA,0.1)
y1=(-1)*np.sqrt(AlCircle[0][2]**2-x1**2)+AlCircle[0][3]
# f2(x) for upper clipping area border line
x2=np.arange(xA,-xA,-0.1)
y2=(+1)*np.sqrt(rWkSt**2-x2**2)
# building clipping area
x3=np.concatenate((x1,x2))
y3=np.concatenate((y1,y2))
poly = Polygon(np.column_stack([x3, y3]), animated=True, color="aqua", fill=False)
plt.gcf().gca().add_artist(poly) # plotting of clipping area
# calculating AzCircles and plotting
circle2=plt.Circle((-AzCircle[i][0],Ze-DZ),AzCircle[i][1],color='g',fill=False)
plt.gcf().gca().add_artist(circle2)
circle2.set_clip_path(poly) # performing clipping
i=i+1
plt.savefig("myPlot.png")
plt.show()
myPlot

Modify matplotlib colormap

I'm trying to produce a similar version of this image using Python:
I'm close but can't quite figure out how to modify a matplotlib colormap to make values <0.4 go to white. I tried masking those values and using set_bad but I ended up with a real blocky appearance, losing the nice smooth contours seen in the original image.
Result with continuous colormap (problem: no white):
Result with set_bad (problem: no smooth transition to white):
Code so far:
from netCDF4 import Dataset as NetCDFFile
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap
nc = NetCDFFile('C:/myfile1.nc')
nc1 = NetCDFFile('C:/myfile2.nc')
lat = nc.variables['lat'][:]
lon = nc.variables['lon'][:]
time = nc.variables['time'][:]
uwnd = nc.variables['uwnd'][:]
vwnd = nc1.variables['vwnd'][:]
map = Basemap(llcrnrlon=180.,llcrnrlat=0.,urcrnrlon=340.,urcrnrlat=80.)
lons,lats = np.meshgrid(lon,lat)
x,y = map(lons,lats)
speed = np.sqrt(uwnd*uwnd+vwnd*vwnd)
#speed = np.ma.masked_where(speed < 0.4, speed)
#cmap = plt.cm.jet
#cmap.set_bad(color='white')
levels = np.arange(0.0,3.0,0.1)
ticks = np.arange(0.0,3.0,0.2)
cs = map.contourf(x,y,speed[0],levels, cmap='jet')
vw = plt.quiver(x,y,speed)
cbar = plt.colorbar(cs, orientation='horizontal', cmap='jet', spacing='proportional',ticks=ticks)
cbar.set_label('850 mb Vector Wind Anomalies (m/s)')
map.drawcoastlines()
map.drawparallels(np.arange(20,80,20),labels=[1,1,0,0], linewidth=0.5)
map.drawmeridians(np.arange(200,340,20),labels=[0,0,0,1], linewidth=0.5)
#plt.show()
plt.savefig('phase8_850wind_anom.png',dpi=600)
The answer to get the result smooth lies in constructing your own colormap. To do this one has to create an RGBA-matrix: a matrix with on each row the amount (between 0 and 1) of Red, Green, Blue, and Alpha (transparency; 0 means that the pixel does not have any coverage information and is transparent).
As an example the distance to some point is plotted in two dimensions. Then:
For any distance higher than some critical value, the colors will be taken from a standard colormap.
For any distance lower than some critical value, the colors will linearly go from white to the first color of the previously mentioned map.
The choices depend fully on what you want to show. The colormaps and their sizes depend on your problem. For example, you can choose different types of interpolation: linear, exponential, ...; single- or multi-color colormaps; etc..
The code:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
# create colormap
# ---------------
# create a colormap that consists of
# - 1/5 : custom colormap, ranging from white to the first color of the colormap
# - 4/5 : existing colormap
# set upper part: 4 * 256/4 entries
upper = mpl.cm.jet(np.arange(256))
# set lower part: 1 * 256/4 entries
# - initialize all entries to 1 to make sure that the alpha channel (4th column) is 1
lower = np.ones((int(256/4),4))
# - modify the first three columns (RGB):
# range linearly between white (1,1,1) and the first color of the upper colormap
for i in range(3):
lower[:,i] = np.linspace(1, upper[0,i], lower.shape[0])
# combine parts of colormap
cmap = np.vstack(( lower, upper ))
# convert to matplotlib colormap
cmap = mpl.colors.ListedColormap(cmap, name='myColorMap', N=cmap.shape[0])
# show some example
# -----------------
# open a new figure
fig, ax = plt.subplots()
# some data to plot: distance to point at (50,50)
x,y = np.meshgrid(np.linspace(0,99,100),np.linspace(0,99,100))
z = (x-50)**2. + (y-50)**2.
# plot data, apply colormap, set limit such that our interpretation is correct
im = ax.imshow(z, interpolation='nearest', cmap=cmap, clim=(0,5000))
# add a colorbar to the bottom of the image
div = make_axes_locatable(ax)
cax = div.append_axes('bottom', size='5%', pad=0.4)
cbar = plt.colorbar(im, cax=cax, orientation='horizontal')
# save/show the image
plt.savefig('so.png')
plt.show()
The result:

streamplot does not work with matplotlib basemap

I am trying to use streamplot function to plot wind field with basemap, projection "ortho". My test code is mainly based on this example:
Plotting wind vectors and wind barbs
Here is my code:
import numpy as np
import matplotlib.pyplot as plt
import datetime
from mpl_toolkits.basemap import Basemap, shiftgrid
from Scientific.IO.NetCDF import NetCDFFile as Dataset
# specify date to plot.
yyyy=1993; mm=03; dd=14; hh=00
date = datetime.datetime(yyyy,mm,dd,hh)
# set OpenDAP server URL.
URLbase="http://nomads.ncdc.noaa.gov/thredds/dodsC/modeldata/cmd_pgbh/"
URL=URLbase+"%04i/%04i%02i/%04i%02i%02i/pgbh00.gdas.%04i%02i%02i%02i.grb2" %\
(yyyy,yyyy,mm,yyyy,mm,dd,yyyy,mm,dd,hh)
data = Dataset(URL)
#data = netcdf.netcdf_file(URL)
# read lats,lons
# reverse latitudes so they go from south to north.
latitudes = data.variables['lat'][:][::-1]
longitudes = data.variables['lon'][:].tolist()
# get wind data
uin = data.variables['U-component_of_wind_height_above_ground'][:].squeeze()
vin = data.variables['V-component_of_wind_height_above_ground'][:].squeeze()
# add cyclic points manually (could use addcyclic function)
u = np.zeros((uin.shape[0],uin.shape[1]+1),np.float64)
u[:,0:-1] = uin[::-1]; u[:,-1] = uin[::-1,0]
v = np.zeros((vin.shape[0],vin.shape[1]+1),np.float64)
v[:,0:-1] = vin[::-1]; v[:,-1] = vin[::-1,0]
longitudes.append(360.); longitudes = np.array(longitudes)
# make 2-d grid of lons, lats
lons, lats = np.meshgrid(longitudes,latitudes)
# make orthographic basemap.
m = Basemap(resolution='c',projection='ortho',lat_0=60.,lon_0=-60.)
# create figure, add axes
fig1 = plt.figure(figsize=(8,10))
ax = fig1.add_axes([0.1,0.1,0.8,0.8])
# define parallels and meridians to draw.
parallels = np.arange(-80.,90,20.)
meridians = np.arange(0.,360.,20.)
# first, shift grid so it goes from -180 to 180 (instead of 0 to 360
# in longitude). Otherwise, interpolation is messed up.
ugrid,newlons = shiftgrid(180.,u,longitudes,start=False)
vgrid,newlons = shiftgrid(180.,v,longitudes,start=False)
# now plot.
lonn, latt = np.meshgrid(newlons, latitudes)
x, y = m(lonn, latt)
st = plt.streamplot(x, y, ugrid, vgrid, color='r', latlon='True')
# draw coastlines, parallels, meridians.
m.drawcoastlines(linewidth=1.5)
m.drawparallels(parallels)
m.drawmeridians(meridians)
# set plot title
ax.set_title('SLP and Wind Vectors '+str(date))
plt.show()
After running the code, I got a blank map with a red smear in the lower left corner (please see the figure). After zoom this smear out, I can see the wind stream in a flat projection (not in "ortho" projection) So I guess this is the problem of data projection on the map. I did tried function transform_vector but it does not solve the problem Can anybody tell me, what did I do wrong, please! Thank you.
A new map after updating code:
You are plotting lat/lon coordinates on a map with an orthographic projection. Normally you can fix this by changing your plotting command to:
m.streamplot(mapx, mapy, ugrid, vgrid, color='r', latlon=True)
But your coordinate arrays don't have the same dimensions, that needs to be fixed as well.

color matplotlib map using bicubic interpolation

I know that matplotlib and scipy can do bicubic interpolation:
http://matplotlib.org/examples/pylab_examples/image_interp.html
http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html
http://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp2d.html
I also know that it is possible to draw a map of the world with matplotlib:
http://matplotlib.org/basemap/users/geography.html
http://matplotlib.org/basemap/users/examples.html
http://matplotlib.org/basemap/api/basemap_api.html
But can I do a bicubic interpolation based on 4 data points and only color the land mass?
For example using these for 4 data points (longitude and latitude) and colors:
Lagos: 6.453056, 3.395833; red HSV 0 100 100 (or z = 0)
Cairo: 30.05, 31.233333; green HSV 90 100 100 (or z = 90)
Johannesburg: -26.204444, 28.045556; cyan HSV 180 100 100 (or z = 180)
Mogadishu: 2.033333, 45.35; purple HSV 270 100 100 (or z = 270)
I am thinking that it must be possible to do the bicubic interpolation across the range of latitudes and longitudes and then add oceans, lakes and rivers on top of that layer? I can do this with drawmapboundary. Actually there is an option maskoceans for this:
http://matplotlib.org/basemap/api/basemap_api.html#mpl_toolkits.basemap.maskoceans
I can interpolate the data like this:
xnew, ynew = np.mgrid[-1:1:70j, -1:1:70j]
tck = interpolate.bisplrep(x, y, z, s=0)
znew = interpolate.bisplev(xnew[:,0], ynew[0,:], tck)
Or with scipy.interpolate.interp2d:
http://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp2d.html
Here it is explained how to convert to map projection coordinates:
http://matplotlib.org/basemap/users/mapcoords.html
But I need to figure out how to do this for a calculated surface instead of individual points. Actually there is an example of such a topographic map using external data, which I should be able to replicate:
http://matplotlib.org/basemap/users/examples.html
P.S. I am not looking for a complete solution. I would much prefer to solve this myself. Rather I am looking for suggestions and hints. I have been using gnuplot for more than 10 years and only switched to matplotlib within the past few weeks, so please don't assume I know even the simplest things about matplotlib.
I think this is what you are looking for (roughly). Note the crucial things are masking the data array before you plot the pcolor and passing in the hsv colormap (Docs: cmap parameter for pcolormesh and available colormaps).
I've kept the code for plotting the maps quite close to the examples so it should be easy to follow. I've kept your interpolation code for the same reason. Note that the interpolation is linear rather than cubic - kx=ky=1 - because you don't give enough points to do cubic interpolation (you'd need at least 16 - scipy will complain with less saying that "m must be >= (kx+1)(ky+1)", although the constraint is not mentioned in the documentation).
I've also extended the range of your meshgrid and kept in lat / lon for x and y throughout.
Code
from mpl_toolkits.basemap import Basemap,maskoceans
import matplotlib.pyplot as plt
import numpy as np
from scipy import interpolate
# set up orthographic map projection with
# perspective of satellite looking down at 0N, 20W (Africa in main focus)
# use low resolution coastlines.
map = Basemap(projection='ortho',lat_0=0,lon_0=20,resolution='l')
# draw coastlines, country boundaries
map.drawcoastlines(linewidth=0.25)
map.drawcountries(linewidth=0.25)
# Optionally (commented line below) give the map a fill colour - e.g. a blue sea
#map.drawmapboundary(fill_color='aqua')
# draw lat/lon grid lines every 30 degrees.
map.drawmeridians(np.arange(0,360,30))
map.drawparallels(np.arange(-90,90,30))
data = {'Lagos': (6.453056, 3.395833,0),
'Cairo': (30.05, 31.233333,90),
'Johannesburg': (-26.204444, 28.045556,180),
'Mogadishu': (2.033333, 45.35, 270)}
x,y,z = zip(*data.values())
xnew, ynew = np.mgrid[-30:60:0.1, -50:50:0.1]
tck = interpolate.bisplrep(x, y, z, s=0,kx=1,ky=1)
znew = interpolate.bisplev(xnew[:,0], ynew[0,:], tck)
znew = maskoceans(xnew, ynew, znew)
col_plot = map.pcolormesh(xnew, ynew, znew, latlon=True, cmap='hsv')
plt.show()
Output
Observe that doing the opposite, that is putting a raster on the sea and lay a mask over the continents, is easy as pie. Simply use map.fillcontinents(). So the basic idea of this solution is to modify the fillcontinents function so that it lays polygons over the oceans.
The steps are:
Create a large circle-like polygon that covers the entire globe.
Create a polygon for each shape in the map.coastpolygons array.
Cut the shape of the landmass polygon away from the circle using shapely and its difference method.
Add the remaining polygons, which have the shape of the oceans, on the top, with a high zorder.
The code:
from mpl_toolkits.basemap import Basemap
import numpy as np
from scipy import interpolate
from shapely.geometry import Polygon
from descartes.patch import PolygonPatch
def my_circle_polygon( (x0, y0), r, resolution = 50 ):
circle = []
for theta in np.linspace(0,2*np.pi, resolution):
x = r * np.cos(theta) + x0
y = r * np.sin(theta) + y0
circle.append( (x,y) )
return Polygon( circle[:-1] )
def filloceans(the_map, color='0.8', ax=None):
# get current axes instance (if none specified).
if not ax:
ax = the_map._check_ax()
# creates a circle that covers the world
r = 0.5*(map.xmax - map.xmin) # + 50000 # adds a little bit of margin
x0 = 0.5*(map.xmax + map.xmin)
y0 = 0.5*(map.ymax + map.ymin)
oceans = my_circle_polygon( (x0, y0) , r, resolution = 100 )
# for each coastline polygon, gouge it out of the circle
for x,y in the_map.coastpolygons:
xa = np.array(x,np.float32)
ya = np.array(y,np.float32)
xy = np.array(zip(xa.tolist(),ya.tolist()))
continent = Polygon(xy)
## catches error when difference with lakes
try:
oceans = oceans.difference(continent)
except:
patch = PolygonPatch(continent, color="white", zorder =150)
ax.add_patch( patch )
for ocean in oceans:
sea_patch = PolygonPatch(ocean, color="blue", zorder =100)
ax.add_patch( sea_patch )
########### DATA
x = [3.395833, 31.233333, 28.045556, 45.35 ]
y = [6.453056, 30.05, -26.204444, 2.033333]
z = [0, 90, 180, 270]
# set up orthographic map projection
map = Basemap(projection='ortho', lat_0=0, lon_0=20, resolution='l')
## Plot the cities on the map
map.plot(x,y,".", latlon=1)
# create a interpolated mesh and set it on the map
interpol_func = interpolate.interp2d(x, y, z, kind='linear')
newx = np.linspace( min(x), max(x) )
newy = np.linspace( min(y), max(y) )
X,Y = np.meshgrid(newx, newy)
Z = interpol_func(newx, newy)
map.pcolormesh( X, Y, Z, latlon=1, zorder=3)
filloceans(map, color="blue")
Voilà:

How can I rotate vectors onto a Basemap map projection?

I'm making wind vector barb plots using the matplotlib barb function and basemap in python.
I have a list of vectors (wind observations) at arbitrary latitudes and longitudes, i.e. not on a regular grid.
I need to rotate the vectors onto the map projection before plotting or the barbs point in the wrong direction. What is the best way to do this?
e.g.
import numpy
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
# Define locations of my vectors
lat = numpy.array([50.1,46.2,51.6,52.2,54.4])
lon = numpy.array([-3.3,-1.0,-5.2,-1.2,0.2])
# Define some east-west vectors to illustrate the problem
u = numpy.array([5,5,5,5,5])
v = numpy.array([0,0,0,0,0])
# Set up map projection
m = Basemap(llcrnrlon=-15.,llcrnrlat=46.,urcrnrlon=15.,urcrnrlat=59.,
projection='lcc',lat_1=40.,lat_2=50.,lon_0=-50.,
resolution ='l')
# Calculate positions of vectors on map projection
x,y = m(lon,lat)
# Draw barbs
m.barbs(x,y,u,v, length=7, color='red')
# Draw some grid lines for reference
parallels = numpy.arange(-80.,90,20.)
meridians = numpy.arange(0.,360.,20.)
m.drawparallels(parallels)
m.drawmeridians(meridians)
m.drawcoastlines(linewidth=0.5)
plt.show()
Note that in the plot, the vectors do not point east-west.
I have tried using the rotate_vector and transform_vector routines, but these only work for gridded vector data.
Is there a routine to rotate the vectors onto the map projection for an arbitrary list of lat,lon u,v pairs?
Any help would be much appreciated!
For people with gridded data who stumpled upon this question
Rather use the built-in function rotate_vector, you can find it here:
http://matplotlib.org/basemap/api/basemap_api.html
Your problem is that you're specifying your u and v in lat, long. At the same time, you're specifying your x and y in map coordinates. barbs seems to expect both of them in map coordinates, rather than a mix.
The simplest way is to just calculate the endpoints to get the components. (My description makes no sense, so here's what I had in mind:)
x, y = m(lon, lat)
x1, y1 = m(lon+u, lat+v)
u_map, v_map = x1-x, y1-y
You'll then need to rescale the magnitudes, as well. As a full example:
import numpy
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
# Define locations of my vectors
lat = numpy.array([50.1,46.2,51.6,52.2,54.4])
lon = numpy.array([-3.3,-1.0,-5.2,-1.2,0.2])
# Define some east-west vectors to illustrate the problem
u = numpy.array([5,5,5,5,5])
v = numpy.array([0,0,0,0,0])
# Set up map projection
m = Basemap(llcrnrlon=-15.,llcrnrlat=46.,urcrnrlon=15.,urcrnrlat=59.,
projection='lcc',lat_1=40.,lat_2=50.,lon_0=-50.,
resolution ='l')
# Calculate positions of vectors on map projection
x,y = m(lon,lat)
# Calculate the orientation of the vectors
x1, y1 = m(lon+u, lat+v)
u_map, v_map = x1-x, y1-y
# Rescale the magnitudes of the vectors...
mag_scale = np.hypot(u_map, v_map) / np.hypot(u, v)
u_map /= mag_scale
v_map /= mag_scale
# Draw barbs
m.barbs(x,y,u_map,v_map, length=7, color='red')
# Draw some grid lines for reference
parallels = numpy.arange(-80.,90,20.)
meridians = numpy.arange(0.,360.,20.)
m.drawparallels(parallels)
m.drawmeridians(meridians)
m.drawcoastlines(linewidth=0.5)
plt.show()

Categories

Resources