For the past few days I have been trying to get weather station data interpolated in a map for my country only. I do this as follows:
Loading the data, I create a grid using interpolation
Based on this grid I draw a contour and contourf image
I then draw shapefiles for Germany, Belgium and France on top of the map in order to cover the irrelevant contour/contourf elements. I used this tutorial for that.
Finally, I use an oceans shapefile (IHO Sea Areas; www.marineregions.org/downloads.php#iho) to plot as well in order to cover the North Sea. Using QGIS, I edited this oceans shapefile and removed everything except for the North Sea - given time constraints :)
You would say that everything goes fine -- but for some reason parts of the country as well as the islands are interpreted as being water. I guess this is because these are distinct parts, all not connected to the main land (due to waters/rivers).
Strangely enough, the edges are drawn, but they are not filled.
I've tried and searched a lot but have no clue about how to fix this. I guess it is somewhere in the LineCollection because in QGIS the shapefile is correct (i.e. it recognizes no shape when clicking the islands etc., which is correct since it should only recognize a shape when clicking on the sea).
I sincerely hope that you could help me point out where I'm wrong and how I can fix this!
Thanks a lot!
This is the map that I'm getting:
https://i.imgur.com/GHISN7n.png
My code is as follows (and you can probably see that I'm very new to this kind of programming, I started yesterday :)):
import numpy as np
import matplotlib
matplotlib.use('Agg')
from scipy.interpolate import griddata
from mpl_toolkits.basemap import Basemap, maskoceans
import matplotlib.pyplot as plt
from numpy.random import seed
import shapefile as shp
from matplotlib.collections import LineCollection
from matplotlib import cm
# Set figure size
plt.figure(figsize=(15,15), dpi=80)
# Define map bounds
xMin, xMax = 2.5, 8.0
yMin, yMax = 50.6, 53.8
# Create map
m = Basemap(projection='merc',llcrnrlon=xMin,llcrnrlat=yMin,urcrnrlon=xMax,urcrnrlat=yMax,resolution='h')
m.drawmapboundary(fill_color='#d4dadc',linewidth=0.25)
# m.drawcoastlines(linewidth=0.5,color='#333333')
# Load data
y = [54.325666666667,52.36,53.269444444444,55.399166666667,54.116666666667,53.614444444444,53.491666666667,53.824130555556,52.918055555556,54.03694,52.139722,52.926865008825,54.853888888889,52.317222,53.240026656696,52.642696895243,53.391265948394,52.505333893732,52.098821802977,52.896643913235,52.457270486008,53.223000488316,52.701902388132,52.0548617826,53.411581103636,52.434561756559,52.749056395511,53.123676213651,52.067534268959,53.194409573306,52.27314817052,51.441334059998,51.224757511326,51.990941918858,51.447744494043,51.960667359998,51.969031121385,51.564889021961,51.857593837453,51.449772459909,51.658528382201,51.196699902606,50.905256257898,51.497306260089,yMin,yMin,yMax,yMax]
x = [2.93575,3.3416666666667,3.6277777777778,3.8102777777778,4.0122222222222,4.9602777777778,5.9416666666667,2.9452777777778,4.1502777777778,6.04167,4.436389,4.7811453228565,4.6961111111111,4.789722,4.9207907082729,4.9787572406902,5.3458010937365,4.6029300588208,5.1797058644882,5.383478899702,5.5196324030324,5.7515738887123,5.8874461671401,5.8723225499118,6.1990994508938,6.2589770334531,6.5729701105864,6.5848470019087,6.6567253619722,7.1493220605216,6.8908745111116,3.5958241584686,3.8609657214986,4.121849767852,4.342014,4.4469005114756,4.9259216999194,4.9352386335384,5.1453989235756,5.3770039280214,5.7065946674719,5.7625447234516,5.7617834850481,6.1961067840608,xMin,xMax,xMin,xMax]
z = [4.8,5.2,5.8,5.4,5,5.3,5.4,4.6,5.8,6.3,4.8,5.4,5.3,4.6,5.4,4.4,4.1,5.5,4.5,4.2,3.9,3.7,4.2,3.2,4,3.8,2.7,2.3,3.4,2.5,3.7,5.2,2.9,5.1,3.8,4.4,4.2,3.9,3.8,3.2,2.6,2.8,2.4,3.1]
avg = np.average(z)
z.extend([avg,avg,avg,avg])
x,y = m(x,y)
# target grid to interpolate to
xis = np.arange(min(x),max(x),2000)
yis = np.arange(min(y),max(y),2000)
xi,yi = np.meshgrid(xis,yis)
# interpolate
zi = griddata((x,y),z,(xi,yi),method='cubic')
# Decide on proper values for colour bar (todo)
vrange = max(z)-min(z)
mult = 2
vmin = min(z)-(mult*vrange)
vmax = max(z)+(mult*vrange)
# Draw contours
cs = m.contour(xi, yi, zi, 5, linewidths=0.25, colors='k')
cs = m.contourf(xi, yi, zi, 5,vmax=vmax,vmin=vmin,cmap=plt.get_cmap('jet'))
# Plot seas from shapefile
sf = shp.Reader(r'/DIR_TO_SHP/shapefiles/northsea')
shapes = sf.shapes()
print shapes[0].parts
records = sf.records()
ax = plt.subplot(111)
for record, shape in zip(records,shapes):
lons,lats = zip(*shape.points)
data = np.array(m(lons, lats)).T
print len(shape.parts)
if len(shape.parts) == 1:
segs = [data,]
else:
segs = []
for i in range(1,len(shape.parts)):
index = shape.parts[i-1]
index2 = shape.parts[i]
segs.append(data[index:index2])
segs.append(data[index2:])
lines = LineCollection(segs,antialiaseds=(1,),zorder=3)
lines.set_facecolors('#abc0d3')
lines.set_edgecolors('red')
lines.set_linewidth(0.5)
ax.add_collection(lines)
# Plot France from shapefile
sf = shp.Reader(r'/DIR_TO_SHP/shapefiles/FRA_adm0')
shapes = sf.shapes()
records = sf.records()
ax = plt.subplot(111)
for record, shape in zip(records,shapes):
lons,lats = zip(*shape.points)
data = np.array(m(lons, lats)).T
if len(shape.parts) == 1:
segs = [data,]
else:
segs = []
for i in range(1,len(shape.parts)):
index = shape.parts[i-1]
index2 = shape.parts[i]
segs.append(data[index:index2])
segs.append(data[index2:])
lines = LineCollection(segs,antialiaseds=(1,))
lines.set_facecolors('#fafaf8')
lines.set_edgecolors('#333333')
lines.set_linewidth(0.5)
ax.add_collection(lines)
# Plot Belgium from shapefile
sf = shp.Reader(r'/DIR_TO_SHP/shapefiles/BEL_adm0')
shapes = sf.shapes()
records = sf.records()
ax = plt.subplot(111)
for record, shape in zip(records,shapes):
lons,lats = zip(*shape.points)
data = np.array(m(lons, lats)).T
if len(shape.parts) == 1:
segs = [data,]
else:
segs = []
for i in range(1,len(shape.parts)):
index = shape.parts[i-1]
index2 = shape.parts[i]
segs.append(data[index:index2])
segs.append(data[index2:])
lines = LineCollection(segs,antialiaseds=(1,))
lines.set_facecolors('#fafaf8')
lines.set_edgecolors('#333333')
lines.set_linewidth(0.5)
ax.add_collection(lines)
# Plot Germany from shapefile
sf = shp.Reader(r'/DIR_TO_SHP/shapefiles/DEU_adm0')
shapes = sf.shapes()
records = sf.records()
ax = plt.subplot(111)
for record, shape in zip(records,shapes):
lons,lats = zip(*shape.points)
data = np.array(m(lons, lats)).T
if len(shape.parts) == 1:
segs = [data,]
else:
segs = []
for i in range(1,len(shape.parts)):
index = shape.parts[i-1]
index2 = shape.parts[i]
segs.append(data[index:index2])
segs.append(data[index2:])
lines = LineCollection(segs,antialiaseds=(1,))
lines.set_facecolors('#fafaf8')
lines.set_edgecolors('#333333')
lines.set_linewidth(0.5)
ax.add_collection(lines)
# Finish plot
plt.axis('off')
plt.savefig("test2.png",bbox_inches='tight',pad_inches=0);
Your problem is that LineCollection does not do what you think it does. What you want is an outer filled shape with 'holes punched in' that have the shapes of the other lines in your LineCollection (i.e. the islands in the north sea). LineCollection, however, fills every line segment in the list, so the filled shapes just overlay each other.
Inspired by this post, I wrote an answer that seems to solve your problem using Patches. However, I'm not entirely sure how robust the solution is: according to the linked (unanswered) post, the vertices of the outer shape should be ordered clockwise while the vertices of the 'punched in' holes should be ordered counter-clock wise (I checked that too and it appears to be correct; this matplotlib example shows the same behaviour). As the shapefiles are binary, it is hard to verify the ordering of the vertices, but the result appears to be correct. In the example below I assume that in each shapefile the longest line segment is the outline of the country (or north sea), while shorter segments are islands or some such. I thus first order the segments of each shapefile by length and create a Path and a PathPatch thereafter. I took the freedom to use a different colour for each shapefile in order to make sure that everything works as it should.
import numpy as np
import matplotlib
matplotlib.use('Agg')
from scipy.interpolate import griddata
from mpl_toolkits.basemap import Basemap, maskoceans
import matplotlib.pyplot as plt
from numpy.random import seed
import shapefile as shp
from matplotlib.collections import LineCollection
from matplotlib.patches import Path, PathPatch
from matplotlib import cm
# Set figure size
fig, ax = plt.subplots(figsize=(15,15), dpi = 80)
# Define map bounds
xMin, xMax = 2.5, 8.0
yMin, yMax = 50.6, 53.8
shapefiles = [
'shapefiles/BEL_adm0',
'shapefiles/FRA_adm0',
'shapefiles/DEU_adm0',
'shapefiles/northsea',
]
colors = ['red', 'green', 'yellow', 'blue']
y = [54.325666666667,52.36,53.269444444444,55.399166666667,54.116666666667,53.614444444444,53.491666666667,53.824130555556,52.918055555556,54.03694,52.139722,52.926865008825,54.853888888889,52.317222,53.240026656696,52.642696895243,53.391265948394,52.505333893732,52.098821802977,52.896643913235,52.457270486008,53.223000488316,52.701902388132,52.0548617826,53.411581103636,52.434561756559,52.749056395511,53.123676213651,52.067534268959,53.194409573306,52.27314817052,51.441334059998,51.224757511326,51.990941918858,51.447744494043,51.960667359998,51.969031121385,51.564889021961,51.857593837453,51.449772459909,51.658528382201,51.196699902606,50.905256257898,51.497306260089,yMin,yMin,yMax,yMax]
x = [2.93575,3.3416666666667,3.6277777777778,3.8102777777778,4.0122222222222,4.9602777777778,5.9416666666667,2.9452777777778,4.1502777777778,6.04167,4.436389,4.7811453228565,4.6961111111111,4.789722,4.9207907082729,4.9787572406902,5.3458010937365,4.6029300588208,5.1797058644882,5.383478899702,5.5196324030324,5.7515738887123,5.8874461671401,5.8723225499118,6.1990994508938,6.2589770334531,6.5729701105864,6.5848470019087,6.6567253619722,7.1493220605216,6.8908745111116,3.5958241584686,3.8609657214986,4.121849767852,4.342014,4.4469005114756,4.9259216999194,4.9352386335384,5.1453989235756,5.3770039280214,5.7065946674719,5.7625447234516,5.7617834850481,6.1961067840608,xMin,xMax,xMin,xMax]
z = [4.8,5.2,5.8,5.4,5,5.3,5.4,4.6,5.8,6.3,4.8,5.4,5.3,4.6,5.4,4.4,4.1,5.5,4.5,4.2,3.9,3.7,4.2,3.2,4,3.8,2.7,2.3,3.4,2.5,3.7,5.2,2.9,5.1,3.8,4.4,4.2,3.9,3.8,3.2,2.6,2.8,2.4,3.1]
avg = np.average(z)
z.extend([avg,avg,avg,avg])
# Create map
m = Basemap(
ax = ax,
projection='merc',
llcrnrlon=xMin,
llcrnrlat=yMin,
urcrnrlon=xMax,
urcrnrlat=yMax,
resolution='h'
)
x,y = m(x,y)
m.drawmapboundary(fill_color='#d4dadc',linewidth=0.25)
# target grid to interpolate to
xis = np.arange(min(x),max(x),2000)
yis = np.arange(min(y),max(y),2000)
xi,yi = np.meshgrid(xis,yis)
# interpolate
zi = griddata((x,y),z,(xi,yi),method='cubic')
# Decide on proper values for colour bar (todo)
vrange = max(z)-min(z)
mult = 2
vmin = min(z)-(mult*vrange)
vmax = max(z)+(mult*vrange)
# Draw contours
cs = m.contour(xi, yi, zi, 5, linewidths=0.25, colors='k')
cs = m.contourf(xi, yi, zi, 5,vmax=vmax,vmin=vmin,cmap=plt.get_cmap('jet'))
for sf_name,color in zip(shapefiles, colors):
print(sf_name)
# Load data
#drawing shapes:
sf = shp.Reader(sf_name)
shapes = sf.shapes()
##print shapes[0].parts
records = sf.records()
##ax = plt.subplot(111)
for record, shape in zip(records,shapes):
lons,lats = zip(*shape.points)
data = np.array(m(lons, lats)).T
if len(shape.parts) == 1:
segs = [data,]
else:
segs = []
for i in range(1,len(shape.parts)):
index = shape.parts[i-1]
index2 = shape.parts[i]
seg = data[index:index2]
segs.append(seg)
segs.append(data[index2:])
##assuming that the longest segment is the enclosing
##line and ordering the segments by length:
lens=np.array([len(s) for s in segs])
order = lens.argsort()[::-1]
segs = [segs[i] for i in order]
##producing the outlines:
lines = LineCollection(segs,antialiaseds=(1,),zorder=4)
##note: leaving the facecolors out:
##lines.set_facecolors('#abc0d3')
lines.set_edgecolors('red')
lines.set_linewidth(0.5)
ax.add_collection(lines)
##producing a path from the line segments:
segs_lin = [v for s in segs for v in s]
codes = [
[Path.MOVETO]+
[Path.LINETO for p in s[1:]]
for s in segs]
codes_lin = [c for s in codes for c in s]
path = Path(segs_lin, codes_lin)
##patch = PathPatch(path, facecolor="#abc0d3", lw=0, zorder = 3)
patch = PathPatch(path, facecolor=color, lw=0, zorder = 3)
ax.add_patch(patch)
plt.axis('off')
fig.savefig("shapefiles.png",bbox_inches='tight',pad_inches=0)
The result looks like this:
Hope this helps.
You haven't plotted the Netherlands.
# Create map
...
# Draw contours
...
# Plot seas from shapefile
sf = shp.Reader(r'/DIR_TO_SHP/shapefiles/northsea')
...
# Plot France from shapefile
sf = shp.Reader(r'/DIR_TO_SHP/shapefiles/FRA_adm0')
...
# Plot Belgium from shapefile
sf = shp.Reader(r'/DIR_TO_SHP/shapefiles/BEL_adm0')
...
# Plot Germany from shapefile
sf = shp.Reader(r'/DIR_TO_SHP/shapefiles/DEU_adm0')
...
# Finish plot
So parts of the Netherlands are filled in with your plotted data, The coastlines are there (from drawing the seas?), but the rest of the country isn't there because you haven't drawn it.
I would steer away from this method of drawing from each shapefile individually, and try to just use basemap for your coastlines, etc. If you must do it from the shapefiles, I would encourage you to define a function that does something like:
def drawContentsOfShapeFile(mymap, path_to_shapefile):
<whatever>
#and use it:
m = Basemap(...)
myshapefiles = []
myshapefiles.append("/DIR_TO_SHP/shapefiles/FRA_adm0")
myshapefiles.append("/DIR_TO_SHP/shapefiles/norway")
for sf in myshapefiles: drawContentOfShapefile(m, sf)
I have a 3d velocity vector field in a numpy array of shape (zlength, ylength, xlength, 3). The '3' contains the velocity components (u,v,w).
I can quite easily plot the vector field in the orthogonal x-y, x-z, and y-z planes using quiver, e.g.
X, Y = np.meshgrid(xvalues, yvalues)
xyfieldfig = plt.figure()
xyfieldax = xyfieldfig.add_subplot(111)
Q1 = xyfieldax.quiver(X, Y, velocity_field[zslice,:,:,0], velocity_field[zslice,:,:,1])
However, I'd like to be able to view the velocity field within an arbitrary plane.
I tried to project the velocity field onto a plane by doing:
projected_field = np.zeros(zlength,ylength,xlength,3)
normal = (nx,ny,nz) #normalised normal to the plane
for i in range(zlength):
for j in range(ylength):
for k in range(xlength):
projected_field[i,j,m] = velocity_field[i,j,m] - np.dot(velocity_field[i,j,m], normal)*normal
However, this (of course) still leaves me with a 3d numpy array with the same shape: (zlength, ylength, xlength, 3). The projected_field now contains velocity vectors at each (x,y,z) position that lie within planes at each local (x,y,z) position.
How do I project velocity_field onto a single plane? Or, how do I now plot my projected_field along one plane?
Thanks in advance!
You're close. Daniel F's suggestion was right, you just need to know how to do the interpolation. Here's a worked example
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
import scipy.interpolate
def norm(v,axis=0):
return np.sqrt(np.sum(v**2,axis=axis))
#Original velocity field
xpoints = np.arange(-.2, .21, 0.05)
ypoints = np.arange(-.2, .21, 0.05)
zpoints = np.arange(-.2, .21, 0.05)
x, y, z = np.meshgrid(xpoints,ypoints,zpoints,indexing='ij')
#Simple example
#(u,v,w) are the components of your velocity field
u = x
v = y
w = z
#Setup a template for the projection plane. z-axis will be rotated to point
#along the plane normal
planex, planey, planez =
np.meshgrid(np.arange(-.2,.2001,.1),
np.arange(-.2,.2001,.1), [0.1],
indexing='ij')
planeNormal = np.array([0.1,0.4,.4])
planeNormal /= norm(planeNormal)
#pick an arbirtrary vector for projection x-axis
u0 = np.array([-(planeNormal[2] + planeNormal[1])/planeNormal[0], 1, 1])
u1 = -np.cross(planeNormal,u0)
u0 /= norm(u0)
u1 /= norm(u1)
#rotation matrix
rotation = np.array([u0,u1,planeNormal]).T
#Rotate plane to get projection vertices
rotatedVertices = rotation.dot( np.array( [planex.flatten(), planey.flatten(), planez.flatten()]) ).T
#Now you can interpolate gridded vector field to rotated vertices
uprime = scipy.interpolate.interpn( (xpoints,ypoints,zpoints), u, rotatedVertices, bounds_error=False )
vprime = scipy.interpolate.interpn( (xpoints,ypoints,zpoints), v, rotatedVertices, bounds_error=False )
wprime = scipy.interpolate.interpn( (xpoints,ypoints,zpoints), w, rotatedVertices, bounds_error=False )
#Projections
cosineMagnitudes = planeNormal.dot( np.array([uprime,vprime,wprime]) )
uProjected = uprime - planeNormal[0]*cosineMagnitudes
vProjected = vprime - planeNormal[1]*cosineMagnitudes
wProjected = wprime - planeNormal[2]*cosineMagnitudes
The number of lines could be reduced using some tensordot operations if you wanted to get fancy. Also this or some close variant it would work without indexing='ij' in meshgrid.
Original field:
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.quiver(x, y, z, u, v, w, length=0.1, normalize=True)
Projected field:
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.quiver(rotatedVertices[:,0], rotatedVertices[:,1], rotatedVertices[:,2],
uprime, vprime,wprime, length=0.5, color='blue', label='Interpolation only')
ax.quiver(rotatedVertices[:,0], rotatedVertices[:,1], rotatedVertices[:,2],
uProjected, vProjected, wProjected, length=0.5, color='red', label='Interpolation + Projection')
plt.legend()
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
I am drawing a map using basemap from matplotlib. The data are spreaded all over the world, but I just want to retain all the data on the continent and drop those on the ocean. Is there a way that I can filter the data, or is there a way to draw the ocean again to cover the data?
There's method in matplotlib.basemap: is_land(xpt, ypt)
It returns True if the given x,y point (in projection coordinates) is over land, False otherwise. The definition of land is based upon the GSHHS coastline polygons associated with the class instance. Points over lakes inside land regions are not counted as land points.
For more information, see here.
is_land() will loop all the polygons to check whether it's land or not. For large data size, it's very slow. You can use points_inside_poly() from matplotlib to check an array of points quickly. Here is the code. It doesn't check lakepolygons, if you want remove points in lakes, you can add your self.
It took 2.7 seconds to check 100000 points on my PC. If you want more speed, you can convert the polygons into a bitmap, but it's a little difficult to do this. Please tell me if the following code is not fast enought for your dataset.
from mpl_toolkits.basemap import Basemap
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.nxutils as nx
def points_in_polys(points, polys):
result = []
for poly in polys:
mask = nx.points_inside_poly(points, poly)
result.extend(points[mask])
points = points[~mask]
return np.array(result)
points = np.random.randint(0, 90, size=(100000, 2))
m = Basemap(projection='moll',lon_0=0,resolution='c')
m.drawcoastlines()
m.fillcontinents(color='coral',lake_color='aqua')
x, y = m(points[:,0], points[:,1])
loc = np.c_[x, y]
polys = [p.boundary for p in m.landpolygons]
land_loc = points_in_polys(loc, polys)
m.plot(land_loc[:, 0], land_loc[:, 1],'ro')
plt.show()
The HYRY's answer won't work on new versions of matplotlib (nxutils is deprecated). I've made a new version that works:
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from matplotlib.path import Path
import numpy as np
map = Basemap(projection='cyl', resolution='c')
lons = [0., 0., 16., 76.]
lats = [0., 41., 19., 51.]
x, y = map(lons, lats)
locations = np.c_[x, y]
polygons = [Path(p.boundary) for p in map.landpolygons]
result = np.zeros(len(locations), dtype=bool)
for polygon in polygons:
result += np.array(polygon.contains_points(locations))
print result
The simplest way is to use basemap's maskoceans.
If for each lat, lon you have a data and you want to
use contours:
After meshgrid and interpolation:
from scipy.interpolate import griddata as gd
from mpl_toolkits.basemap import Basemap, cm, maskoceans
xi, yi = np.meshgrid(xi, yi)
zi = gd((mlon, mlat),
scores,
(xi, yi),
method=grid_interpolation_method)
#mask points on ocean
data = maskoceans(xi, yi, zi)
con = m.contourf(xi, yi, data, cmap=cm.GMT_red2green)
#note instead of zi we have data now.
Update (much faster than in_land or in_polygon solutions):
If for each lat, lon you don't have any data, and you just want to scatter the points only over land:
x, y = m(lons, lats)
samples = len(lons)
ocean = maskoceans(lons, lats, datain=np.arange(samples),
resolution='i')
ocean_samples = np.ma.count_masked(ocean)
print('{0} of {1} points in ocean'.format(ocean_samples, samples))
m.scatter(x[~ocean.mask], y[~ocean.mask], marker='.', color=colors[~ocean.mask], s=1)
m.drawcountries()
m.drawcoastlines(linewidth=0.7)
plt.savefig('a.png')
I was answering this question, when I was told that it would be better to post my answer over here. Basically, my solution extracts the polygons that are used to draw the coastlines of the Basemap instance and combines these polygons with the outline of the map to produce a matplotlib.PathPatch that overlays the ocean areas of the map.
This especially useful if the data is coarse and interpolation of the data is not wanted. In this case using maskoceans produces a very grainy outline of the coastlines, which does not look very good.
Here is the same example I posted as answer for the other question:
from matplotlib import pyplot as plt
from mpl_toolkits import basemap as bm
from matplotlib import colors
import numpy as np
import numpy.ma as ma
from matplotlib.patches import Path, PathPatch
fig, ax = plt.subplots()
lon_0 = 319
lat_0 = 72
##some fake data
lons = np.linspace(lon_0-60,lon_0+60,10)
lats = np.linspace(lat_0-15,lat_0+15,5)
lon, lat = np.meshgrid(lons,lats)
TOPO = np.sin(np.pi*lon/180)*np.exp(lat/90)
m = bm.Basemap(resolution='i',projection='laea', width=1500000, height=2900000, lat_ts=60, lat_0=lat_0, lon_0=lon_0, ax = ax)
m.drawcoastlines(linewidth=0.5)
x,y = m(lon,lat)
pcol = ax.pcolormesh(x,y,TOPO)
##getting the limits of the map:
x0,x1 = ax.get_xlim()
y0,y1 = ax.get_ylim()
map_edges = np.array([[x0,y0],[x1,y0],[x1,y1],[x0,y1]])
##getting all polygons used to draw the coastlines of the map
polys = [p.boundary for p in m.landpolygons]
##combining with map edges
polys = [map_edges]+polys[:]
##creating a PathPatch
codes = [
[Path.MOVETO] + [Path.LINETO for p in p[1:]]
for p in polys
]
polys_lin = [v for p in polys for v in p]
codes_lin = [c for cs in codes for c in cs]
path = Path(polys_lin, codes_lin)
patch = PathPatch(path,facecolor='white', lw=0)
##masking the data:
ax.add_patch(patch)
plt.show()
This produces the following plot:
Hope this is helpful to someone :)