I am trying to regrid my .nc data with irregular grid with the following code:
from mpl_toolkits.basemap import Basemap
from netCDF4 import Dataset as NetCDFFile
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import griddata
nc = NetCDFFile('test.nc')
lat = nc.variables['latitude'][:]
lon = nc.variables['longitude'][:]
time = nc.variables['time'][:]
ssi = nc.variables['solar_irradiation'][:]
XI = np.arange(46.025, 56.525, 0.05)
YI = np.arange(5.025, 15.525, 0.05)
lat_new, lon_new = np.meshgrid(XI, YI)
new_grid = griddata((lat, lon), ssi, (lat_new, lon_new), method='linear')
It works fine, there are NaN values at lat/lon boxes which are not in the original file.
Then I want to plot it using Basemap:
map = Basemap(projection='merc', llcrnrlon=-5., llcrnrlat=35., urcrnrlon=30., urcrnrlat=60.,
resolution='i')
map.drawcountries()
map.drawcoastlines()
x, y = map(XI, YI)
rad = map.contourf(x, y, new_grid)
cb = map.colorbar(rad, "bottom", size="10%", pad="10%")
I am receiving following error: IndexError: too many indices for array: array is 1-dimensional, but 2 were indexed. I know that this means, but I have no clue how to change to code so that it worked the same way. Thank you for every help!
Related
I am trying to plot 2d terrain map with x,y and z (elevation). I followed the steps from the following link but I am getting very weird plot.
Python : 2d contour plot from 3 lists : x, y and rho?
I spent almost half day searching but got nowhere.
import numpy as np
import matplotlib.pyplot as plt
import scipy.interpolate
# import data:
import xlrd
loc = "~/Desktop/Book4.xlsx"
wb = xlrd.open_workbook(loc)
sheet = wb.sheet_by_index(0)
sample=500
# Generate array:
x=np.array(sheet.col_values(0))[0:sample]
y=np.array(sheet.col_values(1))[0:sample]
z=np.hamming(sample)[0:sample][:,None]
# Set up a regular grid of interpolation points
xi, yi = np.meshgrid(x, y)
# Interpolate
rbf = scipy.interpolate.Rbf(x, y, z, function='cubic')
zi = rbf(xi, yi)
# Plot
plt.imshow(zi, vmin=z.min(), vmax=z.max(), origin='lower',
extent=[x.min(), x.max(), y.min(), y.max()])
plt.colorbar()
plt.show()
The first of the following fig is what I am getting and the last one is how it should look like.
Any help shall be appreciated
Link to data file
I think the problem is that the data you're giving it is not smooth enough to interpolate with the default parameters. Here's one approach, using mgrid instead of meshgrid:
import numpy as np
import pandas as pd
from scipy.interpolate import Rbf
# fname is your data, but as a CSV file.
data = pd.read_csv(fname).values
x, y = data.T
x_min, x_max = np.amin(x), np.amax(x)
y_min, y_max = np.amin(y), np.amax(y)
# Make a grid with spacing 0.002.
grid_x, grid_y = np.mgrid[x_min:x_max:0.002, y_min:y_max:0.002]
# Make up a Z.
z = np.hamming(x.size)
# Make an n-dimensional interpolator.
rbfi = Rbf(x, y, z, smooth=2)
# Predict on the regular grid.
di = rbfi(grid_x, grid_y)
Then you can look at the result:
import matplotlib.pyplot as plt
plt.imshow(di)
I get:
I wrote a Jupyter Notebook on this topic recently, check it out for a few other interpolation methods, like kriging and spline fitting.
I'm trying to plot streamlines on a global map with the Robinson projection, but basemap doesn't seem to like the projected co-ordinates. Of course, it works fine for a plain old cylindrical projection, which is regular in the x direction.
Here is an example:
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import numpy as np
u = np.ones((21,21))
v = np.ones((21,21))
lats = np.arange(-90,91,9)
lons = np.arange(-180,181,18)
x,y = np.meshgrid(lons,lats)
# It works for Cylindrical
mp = Basemap(projection='cyl')
xx,yy = mp(x,y)
mp.streamplot(xx,yy,u,v)
mp.drawcoastlines()
plt.show()
# But not Robinson
mp = Basemap(projection='robin',lon_0=0)
xx, yy = mp(x, y)
mp.streamplot(xx,yy,u,v)
mp.drawcoastlines()
plt.show()
It complains about the x co-ordinates, raising:
ValueError: The rows of 'x' must be equal
So is it possible to plot streamlines on Robinson projections?
With the command xx,yy = mp(x,y) a coordinate transformation according to the particular projection is applied to your lon and lats. For most projections this will result in a distorsion of the gird point such that rows of x are no longer equal, hence the error: ValueError: The rows of 'x' must be equal. To fix this you need to re-grid your data, e.g. like this:
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import numpy as np
import matplotlib as plt
u = np.ones((21,21))
v = np.ones((21,21))
lats = np.arange(-90,91,9)
lons = np.arange(-180,181,18)
x,y = np.meshgrid(lons,lats)
mp = Basemap(projection='robin',lon_0=0)
xx, yy = mp(x, y)
# generate a grid that is equally spaced in a plot with the current pojection
lons, lats, xxnew, yynew = mp.makegrid(21,21, returnxy=True)
# project the data onto the new grid
unew = plt.mlab.griddata(xx.flatten(), yy.flatten(),u.flatten(), xxnew, yynew ,interp='linear')
vnew = plt.mlab.griddata(xx.flatten(), yy.flatten(),v.flatten(), xxnew, yynew ,interp='linear')
mp.streamplot(xxnew,yynew,unew,vnew)
mp.drawcoastlines()
plt.show()
I am trying to create a plot like the image using the data attached here, however when I try to plot the grid data I am having a memory error.
I can only plot the the file using scatter option from matplotlib like my following example:
%matplotlib inline
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from scipy.interpolate import griddata
dffold = pd.read_csv('foldmap.toc', delimiter='\t',encoding='latin1',usecols=['Inline','Crossline','X','Y','Fold'])
x1 = np.array(dffold.X)
y1 = np.array(dffold.Y)
zf = np.array(dffold.Fold)
xi = np.linspace(min(x1), max(x1))
yi = np.linspace(min(y1), max(y1))
A, B = np.meshgrid(xi, yi, copy=False)
zi = griddata((x1, y1), zf , (xi, yi), method='nearest')
plt.scatter(x1,y1,zf)
plt.show()
I have some Fortran code which outputs the polar coordinates of a grid on the surface of a sphere in theta, phi format. It also outputs a value associated with each of these points (specifically meant to represent the voltage at that point on the sphere's surface).
Now I want to read this data into Python, plot a sphere, and then colour it according to the voltage data values. I know how to do this for a latitude-longitude grid, but my grid points are not ordered in any specific way.
The code I'm trying is as follows:
import matplotlib.pyplot as plt
from matplotlib import cm, colors
from mpl_toolkits.mplot3d import Axes3D
import option_d
import numpy as np
# Create a sphere
r = 1.0
pi = np.pi
cos = np.cos
sin = np.sin
#Read in grid points
data = np.genfromtxt('grid.txt')
phi, theta = np.hsplit(data, 2)
#Convert grid points to cartesian
x = r*sin(phi)*cos(theta)
y = r*sin(phi)*sin(theta)
z = r*cos(phi)
#Import data from initial state
colorfunction = np.genfromtxt('sphere_init.txt')
print np.shape(colorfunction)
#Normalise the colour map to the initial data
newcm = option_d.test_cm
norm=colors.Normalize(vmin = -np.max(colorfunction), vmax = np.max(colorfunction), clip = False)
#Plot the surface
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(
x,y,z,rstride=1,cstride=1,cmap=newcm,facecolors=newcm(norm(colorfunction)))
#Set axes and display or save
ax.set_aspect("equal")
plt.tight_layout()
plt.show()
The file 'grid.txt' contains two columns, each 770 in length, representing the phi, theta coordinates of each point. The file 'sphere_init.txt' contains a single column of length 770, which are the corresponding data values. However, this does not work - it just throws error messages at me. Is it even possible to plot a sphere from disordered grid points? Any help much appreciated. Thanks.
Edit
Here is the error message:
Traceback (most recent call last):
File "sphere.py", line 43, in <module>
x,y,z,rstride=1,cstride=1, cmap=newcm,facecolors=newcm(norm(colorfunction)))
File "/usr/lib/pymodules/python2.7/mpl_toolkits/mplot3d/axes3d.py", line 1611, in plot_surface
colset.append(fcolors[rs][cs])
IndexError: index out of bounds
I believe I have solved my problem. I read in my irregular grid data, and then also create a regular latitude-longitude grid. I then interpolate from the irregular grid to the lat-long grid:
import matplotlib.pyplot as plt
import matplotlib.mlab as ml
from matplotlib import cm, colors
from mpl_toolkits.mplot3d import Axes3D
import option_d
import numpy as np
import time
#Read in lebedev grid points
data = np.genfromtxt('grid.txt')
u, v = np.hsplit(data, 2)
phi, theta = u[:,0], v[:,0]
#Import data from initial state
colorfunction = np.genfromtxt('sphere_init.txt')
#Generate a lat-long grid to interpolate on
p = np.linspace(0,np.pi, 770)
t = np.linspace(-np.pi, np.pi, 770)
p, t = np.meshgrid(p, t)
#Interpolate using delaunay triangularization
zi = ml.griddata(phi, theta, colorfunction, p, t)
#Convert the lat-long grid points to cartesian
x = np.sin(p)*np.cos(t)
y = np.sin(p)*np.sin(t)
z = np.cos(p)
#Normalize the interpolated colourfunction
#Use fancy new colourmap
newcm = option_d.test_cm
norm=colors.Normalize(vmin = -np.max(zi), vmax = np.max(zi), clip = False)
#Plot the surface
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(
x,y,z,rstride=1,cstride=1, cmap=newcm,facecolors=newcm(norm(zi)))
#Display
ax.set_aspect("equal")
ax.set_xlim([-1,1])
ax.set_ylim([-1,1])
ax.set_zlim([-1,1])
plt.tight_layout()
plt.show()
Edit
I have run into a new problem with this method. It causes a chunk to be missing from the back of my sphere:
Any ideas why?
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 :)