Related
I have a set of satellite data file here, I created a grid for lat & lon and a 2D array for Ozone values.
I know that in order to plot the contourf of the data in a map I need the projection coordinates, but I can't get find a way around it as my grid is not square (144x24). I am covering the geographical area (0 to 360; -30 to 30) and I require square pixels.
The data is quite long to post it but this is my code so far,
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from mpl_toolkits.basemap import Basemap, cm
%matplotlib inline
path = '/home/rafaella/month_files_CSV/O3_COLUMNS_MATCHED_fv0005_200306.csv'
df = pd.read_csv(path, skiprows=1)
df = pd.read_csv(path, delim_whitespace=True)
lat = np.array(df['AVG_LAT'])
lon = np.array(df['AVG_LON'])
toc = np.array(df['TROP_COL'])
#new grid for lon[0,360] lat[-30,30]
lomin = 0
lomax = 360
lamin = -30
lamax = 30
stp = 2.5
loc_lon = np.zeros(int((lomax-lomin)/stp))
loc_lat = np.zeros(int((lamax-lamin)/stp))
for i in range(0,len(loc_lon)):
loc_lon[i] = i*stp +lomin
for j in range(0,len(loc_lat)):
loc_lat[j] = j*stp +lamin
mtoc_local = np.zeros((len(loc_lon),len(loc_lat)))
sdtoc_local = np.zeros((len(loc_lon),len(loc_lat)))
mtoc_local[:,:] = np.nan
sdtoc_local[:,:] = np.nan
for i in range (0, len(loc_lon)):
for j in range (0,len(loc_lat)):
ix = np.where((lon>=loc_lon[i])& (lat>=loc_lat[j]) & (lon<loc_lon[i]+stp) & (lat<loc_lat[j]+stp))[0]
mtoc_local[i,j]=np.nanmean(toc[ix])
sdtoc_local[i,j]=np.nanstd(toc[ix])
fig = plt.figure(figsize=(20, 5))
map = Basemap(llcrnrlon=0,llcrnrlat=-30, urcrnrlon=360.,urcrnrlat=30.,\
rsphere=(6378137.00,6356752.3142),\
resolution='l',projection='merc',\
lat_0=0,lon_0=-30.,lat_ts=30.)
map.drawcoastlines()
# draw parallels
map.drawparallels(np.arange(-30,30,10),labels=[1,1,0,1])
# draw meridians
map.drawmeridians(np.arange(-180,180,20),labels=[1,1,0,1])
map = plt.contourf(loc_lon, loc_lat , mtoc_local.T, vmin=210, vmax=350, cmap='RdPu')
plt.colorbar(orientation='horizontal', ticks=[200, 220, 240, 260, 280, 300, 320, 340] )
plt.title('Tropical TOC monthly mean 06,2009')
plt.show()
It plots very well the map OR the data but not both. here an image of both separately
map
real data
I am very new to python, I started a month ago, so it is still not familiar to me all the functions and libraries.
Your code has two problems. First you have to apply the projection on your coordinates which is done using x,y = map(lon, lat). However, this will raise an error in your case since the dimensions of loc_lon and loc_lat are different. Instead of passing x and y vectors to the contourf function you can pass arrays with the same shape as z (mtoc_local.T). You can use np.meshgrid to create those. Long story short, replace the line with the contourf command with the following three lines
X, Y = np.meshgrid(loc_lon, loc_lat)
x,y = map(X,Y)
map = plt.contourf(x, y , mtoc_local.T, vmin=210, vmax=350, cmap='RdPu')
and the result looks like this
I have file containing points under the columns "x-cord", "y-cord", "value". These are irregularly spaced. I am trying to make a contour plot of "value" and overlay this over the original domain. I gave up trying to do this in both pgfplots and matlab and thought I would give python a go. An answer in any of these scripts would be fine. The python script is as follows
import numpy as np
from scipy.interpolate import griddata
import matplotlib.pyplot as plt
import numpy.ma as ma
from numpy.random import uniform, seed
from scipy.spatial import ConvexHull
#
# Loading data
filename = "strain.dat"
coordinates = []
x_c = []
y_c = []
z_c = []
xyz = open(filename)
title = xyz.readline()
for line in xyz:
x,y,z = line.split()
coordinates.append([float(x), float(y), float(z)])
x_c.append([float(x)])
y_c.append([float(y)])
z_c.append([float(z)])
xyz.close()
#
# Rehaping and translating data
x_c=np.ravel(np.array(x_c))
y_c=np.ravel(np.array(y_c))
z_c=np.ravel(np.array(z_c))
x_c = x_c-100.0
y_c = y_c-100.0
#
# Checking the convex hull
points=np.column_stack((x_c,y_c))
hull = ConvexHull(points);
plt.plot(points[hull.vertices,0], points[hull.vertices,1], 'r--', lw=2)
plt.scatter(x_c, y_c, marker='o', s=5, zorder=10)
#
# Mapping the irregular data onto a regular grid and plotting
xic = np.linspace(min(x_c), max(x_c), 1000)
yic = np.linspace(min(y_c), max(y_c), 1000)
zic = griddata((x_c, y_c), z_c, (xic[None,:], yic[:,None]))
CS = plt.contour(xic,yic,zic,15,linewidths=0.5,colors='k')
CS = plt.contourf(xic,yic,zic,15,cmap=plt.cm.summer)
plt.colorbar() # draw colorbar
#
#plt.scatter(x_c, y_c, marker='o', s=5, zorder=10)
plt.axis('equal')
plt.savefig('foo.pdf', bbox_inches='tight')
plt.show()
and the output looks like
The problem is that griddata uses a convex hull and this convex hull exceeds the edges of the irregular data. Is there any way to set the values of the griddata points which are outside the edges of the boundary of the original points to zero?
Edit
In the end I threw in the towel and reverted back to Matlab. I'll have to export the data to pgfplots to get a nice plot. The code I came up with was
x = strain.x;
y = strain.y;
z = strain.eps;
% Get the alpha shape (couldn't do this in python easily)
shp = alphaShape(x,y,.001);
% Get the boundary nodes
[bi, xy] = boundaryFacets(shp);
no_grid = 500;
xb=xy(:,1);
yb=xy(:,2);
[X,Y] = ndgrid(linspace(min(x),max(x),no_grid),linspace(min(y),max(y),no_grid));
Z = griddata(x,y,z,X,Y,'v4');
% Got through the regular grid and set the values which are outside the boundary of the original domain to Nans
for j = 1:no_grid
[in,on] = inpolygon(X(:,j),Y(:,j),xb,yb);
Z(~in,j) = NaN;
end
contourf(X,Y,Z,10),axis equal
colorbar
hold on
plot(xb,yb)
axis equal
hold off
Here is the resulting image.
If someone can do something similar in Python I'll happily accept the answer.
I had to plot interpolated data on a complex geometry (see the blue points on figure) P(x,z) (z is the horizontal coordinate). I used mask operations and it worked well. Without mask, the whole square (x=0..1 ; z=0..17.28) is covered by contourf.
## limiting values for geometry
xmax1=0.408
zmin1=6.
xmax2=0.064
zmin2=13.12
xmin=0.
xmax=1.
zmin=0.
zmax=17.28
# Grid for points
x1 = np.arange(xmin,xmax+dx,dx)
z1 = np.arange(zmin,zmax+dz,dz)
zi2,xi2 = np.meshgrid(z1,x1)
mask = (((zi2 > zmin2) & (xi2 > xmax2)) | ((zi2 > zmin1) & (zi2 <= zmin2) & (xi2 > xmax1)))
zim=np.ma.masked_array(zi2,mask)
xim=np.ma.masked_array(xi2,mask)
# Grid for P values
# npz=z coordinates of data, npx is the x coordinates and npp is P values
grid_p = scipy.interpolate.griddata((npz, npx), npp, (zim,xim),method='nearest')
pm=np.ma.masked_array(grid_p,mask)
# plot
plt.contour(zim, xim, pm, 25, linewidths=0.5, colors='k',corner_mask=False)
plt.contourf(zim, xim, pm, 25,vmax=grid_p.max(), vmin=grid_p.min(),corner_mask=False)
plt.colorbar()
# Scatter plot to check
plt.scatter(npz,npr, marker='x', s=2)
plt.show()
enter image description here
Sorry if this question is simple I'm a newb to using Python and Basemap. Anyway I'm trying to plot the path of 20 hurricanes on a map (graph). The map itself and the legend show up perfectly but the paths of the hurricanes do not. Also I'm not getting any traceback messages but I think I have an idea of where my problem may be. Could someone please tell me where I went wrong.
Here's a sample of the csv file:
Year, Name, Type, Latitude, Longitude
1957,AUDREY,HU, 21.6, 93.3
1957,AUDREY,HU,22.0, 93.4
1957,AUDREY,HU,22.6, 93.5
1969,AUDREY,HU,28.2,99.6
1957,AUDREY,HU,26.5,93.8
1957,AUDREY,HU,27.9,93.8
1957,AUDREY,HU,29.3,95
1957,AUDREY,HU,27.9,93.8
1957,AUDREY,HU,29.3,93.8
1957,AUDREY,HU,30.7,93.5
1969,CAMILLE,HU, 21.6,99.3
1969,CAMILLE,HU,22.0,98.4
1969,CAMILLE,HU,22.6,90.5
1969,CAMILLE,HU,23.2,93.6
Here's the code I have so far:
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import csv, os, scipy
import pandas
from PIL import *
data = np.loadtxt('louisianastormb.csv',dtype=np.str,delimiter=',',skiprows=1)
'''print data'''
fig = plt.figure(figsize=(12,12))
ax = fig.add_axes([0.1,0.1,0.8,0.8])
m = Basemap(llcrnrlon=-100.,llcrnrlat=0.,urcrnrlon=-20.,urcrnrlat=57.,
projection='lcc',lat_1=20.,lat_2=40.,lon_0=-60.,
resolution ='l',area_thresh=1000.)
m.bluemarble()
m.drawcoastlines(linewidth=0.5)
m.drawcountries(linewidth=0.5)
m.drawstates(linewidth=0.5)
# Creates parallels and meridians
m.drawparallels(np.arange(10.,35.,5.),labels=[1,0,0,1])
m.drawmeridians(np.arange(-120.,-80.,5.),labels=[1,0,0,1])
m.drawmapboundary(fill_color='aqua')
color_dict = {'AUDREY': 'red', 'ETHEL': 'white', 'BETSY': 'yellow','CAMILLE': 'blue', 'CARMEN': 'green','BABE': 'purple', }
colnames = ['Year','Name','Type','Latitude','Longitude']
data = pandas.read_csv('louisianastormb.csv', names=colnames)
names = list(data.Name)
lat = list(data.Latitude)
long = list(data.Longitude)
colorName = list(data.Name)
#print lat
#print long
lat.pop(0)
long.pop(0)
colorName.pop(0)
latitude= map(float, lat)
longitude = map(float, long)
x, y = m(latitude,longitude)
#Plots points on map
for colorName in color_dict.keys():
plt.plot(x,y,linestyle ='-',label=colorName,color=color_dict[colorName], linewidth=5 )
lg = plt.legend()
lg.get_frame().set_facecolor('grey')
plt.show()
two (okay I lied, should be there) problems with your code
i, your input longitude should be negative to be within the boundary you defined for your basemap, so add this after before converting to x and y
longitude = [-i for i in longitude]
ii, your coordinate conversion line is wrong, you should swap lon and lat in the argument list
x, y = m(longitude, latitude)
instead of
x, y = m(latitude,longitude)
EDIT:
okay, the second question that OP posted in the comments, please check the complete code below and please pay attention to the changes I've made compared to yours
# Last-modified: 21 Oct 2013 05:35:16 PM
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import csv, os, scipy
import pandas
from PIL import *
data = np.loadtxt('louisianastormb.csv',dtype=np.str,delimiter=',',skiprows=1)
'''print data'''
fig = plt.figure(figsize=(12,12))
ax = fig.add_axes([0.1,0.1,0.8,0.8])
m = Basemap(llcrnrlon=-100.,llcrnrlat=0.,urcrnrlon=-20.,urcrnrlat=57.,
projection='lcc',lat_1=20.,lat_2=40.,lon_0=-60.,
resolution ='l',area_thresh=1000.)
m.drawcoastlines(linewidth=0.5)
m.drawcountries(linewidth=0.5)
m.drawstates(linewidth=0.5)
# m.bluemarble(ax=ax)
# Creates parallels and meridians
m.drawparallels(np.arange(10.,35.,5.),labels=[1,0,0,1])
m.drawmeridians(np.arange(-120.,-80.,5.),labels=[1,0,0,1])
m.drawmapboundary(fill_color='aqua')
color_dict = {'AUDREY': 'red', 'ETHEL': 'white', 'BETSY': 'yellow','CAMILLE': 'blue', 'CARMEN': 'green','BABE': 'purple', }
colnames = ['Year','Name','Type','Latitude','Longitude']
data = pandas.read_csv('louisianastormb.csv', names=colnames)
names = list(data.Name)
lat = list(data.Latitude)
long = list(data.Longitude)
colorNames = list(data.Name)
#print lat
#print long
lat.pop(0)
long.pop(0)
colorNames.pop(0)
latitude= map(float, lat)
longitude = map(float, long)
# added by nye17
longitude = [-i for i in longitude]
# x, y = m(latitude,longitude)
x, y = m(longitude,latitude)
# convert to numpy arrays
x = np.atleast_1d(x)
y = np.atleast_1d(y)
colorNames = np.atleast_1d(colorNames)
#Plots points on map
for colorName in color_dict.keys():
plt.plot(x[colorName == colorNames],y[colorName == colorNames],linestyle ='-',label=colorName,color=color_dict[colorName], linewidth=5 )
lg = plt.legend()
lg.get_frame().set_facecolor('grey')
plt.show()
I think your difficulty is not so much in Basemap as in the plotting. Instead of plotting the entire x/y data set you need to find the x/y points corresponding on hurricane Z. Then plot only those points in a certain color c. Then find the points corresponding to the next hurricane etc...
The below, while not using the Basemap data structure should provide a starting point for plotting subsets of points based on some selector vector.
#given a list of x,y coordinates with a label we'll plot each line individually
#first construct some points to plot
x1 = [1,1.1,1.2,1.3, 2.0,2.2,2.3, 4,3.9,3.8,3.7]
y1 = [5,5.1,5.2,5.3, 6.0,6.2,6.3, 2,2.1,2.2,2.3]
pointNames = []
#generate some labels
pointNames.extend(['a']*4)
pointNames.extend(['b']*3)
pointNames.extend(['c']*4)
#make things easy by casting to numpy arrays to allow for easier indexing
x1 = numpy.array(x1)
y1 = numpy.array(y1)
pointNames = numpy.array(pointNames)
for elem in ['a','b','c']:
selector = pointNames==elem
subsetX = x1[selector]
subsetY = y1[selector]
#now plot subsetX vs subsetY in color Z
plot(subsetX,subsetY,'*-')
show()
I try to display a raster image using gdal and matplotlib-basemap.
I explain here my try using basemap.interp function, for a total structured overview of my process, please look to my IPython Notebook.
First my code to load and project the raster.
# Load Raster
pathToRaster = r'I:\Data\anomaly//ano_DOY2002170.tif'
raster = gdal.Open(pathToRaster, gdal.GA_ReadOnly)
array = raster.GetRasterBand(1).ReadAsArray()
msk_array = np.ma.masked_equal(array, value = 65535)
print 'Raster Projection:\n', raster.GetProjection()
print 'Raster GeoTransform:\n', raster.GetGeoTransform()
# Project raster image using Basemap and the basemap.interp function
map = Basemap(projection='robin',resolution='c',lat_0=0,lon_0=0)
datain = np.flipud( msk_array )
nx = raster.RasterXSize
ny = raster.RasterYSize
xin = np.linspace(map.xmin,map.xmax,nx) # nx is the number of x points on the grid
yin = np.linspace(map.ymin,map.ymax,ny) # ny in the number of y points on the grid
lons = np.arange(-180,180,0.25) #from raster.GetGeoTransform()
lats = np.arange(-90,90,0.25)
lons, lats = np.meshgrid(lons,lats)
xout,yout = map(lons, lats)
dataout = mpl_toolkits.basemap.interp(datain, xin, yin, xout, yout, order=1)
levels = [-1000,-800,-600,-400,-200,0,200,400,600,800,1000]
cntr = map.contourf(xout,yout,dataout, levels,cmap=cm.RdBu)
cbar = map.colorbar(cntr,location='bottom',pad='15%')
# Add some more info to the map
cstl = map.drawcoastlines(linewidth=.5)
meri = map.drawmeridians(np.arange(0,360,60), linewidth=.2, labels=[1,0,0,1], labelstyle='+/-', color='grey' )
para = map.drawparallels(np.arange(-90,90,30), linewidth=.2, labels=[1,0,0,1], labelstyle='+/-', color='grey')
boun = map.drawmapboundary(linewidth=0.5, color='grey')
This will plot the following:
It's especially clear to see that on the east coast of North and South America there is a offset of the raster data and the coastlines.
I'm clueless how to adapt my code so my data will be transformed in the right projection.
For what it's worth: My used raster tif file (if you download it puts an '-' between 'a' and 'no', before 'ano_DOY..' after 'a-no_DOY..')
I'm not sure what you are doing wrong with your own interpolation/reprojecting, but it can be done even simpler.
The contourf accepts a latlon keyword which, when true, accepts lat/lon inputs and converts it to the map projection automatically. So:
datain = msk_array
fig = plt.figure(figsize=(12,5))
map = Basemap(projection='robin',resolution='c',lat_0=0,lon_0=0)
ny, nx = datain.shape
xin = np.linspace(map.xmin,map.xmax,nx) # nx is the number of x points on the grid
yin = np.linspace(map.ymin,map.ymax,ny) # ny in the number of y points on the grid
lons = np.arange(-180,180,0.25) #from raster.GetGeoTransform()
lats = np.arange(90,-90,-0.25)
lons, lats = np.meshgrid(lons,lats)
xx, yy = m(lons,lats)
levels = [-1000,-800,-600,-400,-200,0,200,400,600,800,1000]
cntr = map.contourf(xx, yy,datain, levels,cmap=cm.RdBu)
cbar = map.colorbar(cntr,location='bottom',pad='15%')
# Add some more info to the map
cstl = map.drawcoastlines(linewidth=.5)
meri = map.drawmeridians(np.arange(0,360,60), linewidth=.2, labels=[1,0,0,1], labelstyle='+/-', color='grey' )
para = map.drawparallels(np.arange(-90,90,30), linewidth=.2, labels=[1,0,0,1], labelstyle='+/-', color='grey')
boun = map.drawmapboundary(linewidth=0.5, color='grey')
Note that i changed the lats definition in order to remove the flipping of your input raster, just a personal preference.
Update: I am trying to map some data. I have a set of measured back-azimuths (baz) from a reference point in a grid. I want to find all points on the grid that a great circle along the baz would cross. To do this I iterate through each point in the grid, calculate expected back-azimuth between that point and the reference point and compare to each measured baz. If the difference between the two is small (less than 2 degrees) I weight that point. I then put it all on a map. The code I use is below but the results look a bit strange, does anyone know where I have gone wrong, or if there is a better approach (faster) then what I have done??
from matplotlib.colorbar import ColorbarBase
import matplotlib.cm as cm
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap
import mpl_toolkits.basemap.pyproj as pyproj
llcrnrlon = -30.0
llcrnrlat = 45.0
urcrnrlon = 0.0
urcrnrlat = 65.0
lon_0 = (urcrnrlon + llcrnrlon) / 2.
lat_0 = (urcrnrlat + llcrnrlat) / 2.
lat = 51.58661577 # reference point
lon = -9.18822525
# Generate random back-azimuths.
baz = zeros((20))
for i in xrange(len(baz)):
baz[i] = random.randint(200,230)
####################################################################
## Set up the map background.
m = Basemap(llcrnrlon=llcrnrlon,llcrnrlat=llcrnrlat,urcrnrlon=urcrnrlon,urcrnrlat=urcrnrlat,
resolution='i',projection='lcc',lon_0=lon_0,lat_0=lat_0)
m.drawcoastlines()
m.fillcontinents()
# draw parallels
m.drawparallels(np.arange(10,70,10),labels=[1,0,0,0])
# draw meridians
m.drawmeridians(np.arange(-80, 25, 10),labels=[0,0,0,1])
# Plot station locations.
x, y = m(lon, lat) # array ref points
m.plot(x,y,'ro', ms=5)
####################################################################
## Set up the grids etc.
glons = np.linspace(llcrnrlon, urcrnrlon, 100)
glats = np.linspace(llcrnrlat, urcrnrlat, 100)
# Convert to map coords.
xlons, ylats = m(glons, glats)
# create grid for pcolormesh.
grid_lon, grid_lat = np.meshgrid(xlons, ylats)
# create weights for pcolormesh.
weights = np.zeros(np.shape(grid_lon))
# create grid of lat-lon coords for baz calculation.
gln, glt = np.meshgrid(glons, glats)
####################################################################
## calculate baz from grid_lon, grid_lat to lon, lat. If less
## than error weight grid point.
# method for BAZ calculation via pyproj.
def get_baz(lon1, lat1, lon2, lat2):
g = pyproj.Geod(ellps='WGS84')
az, baz, dist = g.inv(lon1, lat1, lon2, lat2)
return baz
# BAZ calcultion for each point in grid.
ll=0
for mBAZ in baz:
for i in xrange(len(gln)):
for k in xrange(len(gln[i])):
nbaz = get_baz(lon, lat, gln[i][k], glt[i][k])
nbaz += 180
if abs(nbaz - mBAZ) < 2:
weights[i][k] = 1
ll+=1
# plot grid.
m.pcolormesh(grid_lon, grid_lat, weights, cmap=plt.cm.YlOrBr)
plt.colorbar()
plt.show()
Original question below, out of date now.
I am trying to map some data. I have a dataset that gives a range of values (frequencies) for each direction. I want to plot them on a grid so each grid point along a particular azimuth is weighted by the power for a particular frequency.
I have created a map with basemap and plotted a grid over it as follows,
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
from shoot import *
llcrnrlon = -20.0
llcrnrlat = 45.0
urcrnrlon = 10.0
urcrnrlat = 65.0
lon_0 = (urcrnrlon + llcrnrlon) / 2.
lat_0 = (urcrnrlat + llcrnrlat) / 2.
m = Basemap(llcrnrlon=llcrnrlon,llcrnrlat=llcrnrlat,urcrnrlon=urcrnrlon,urcrnrlat=urcrnrlat,
resolution='i',projection='lcc',lon_0=lon_0,lat_0=lat_0)
## Set up the grid.
glons = np.linspace(-20,10,50)
glats = np.linspace(45, 65, 50)
xlons, ylats = m(glons, glats)
grid_lon, grid_lat = np.meshgrid(xlons, ylats)
pwr = np.zeros((50,50))
m.drawcoastlines()
m.fillcontinents()
# draw parallels
m.drawparallels(np.arange(10,70,10),labels=[1,0,0,0])
# draw meridians
m.drawmeridians(np.arange(-80, 25, 10),labels=[0,0,0,1])
lats = [54.8639587, 51.5641564]
lons = [-8.1778180, -9.2754284]
x, y = m(lons, lats) # array ref points
# Plot station locations.
m.plot(x,y,'ro', ms=5)
m.pcolormesh(grid_lon, grid_lat, pwr)
then I shoot out the great circle I want using some functions I found at this nice site
glon1 = lons[0]
glat1 = lats[0]
azimuth = 280.
maxdist = 200.
great(m, glon1, glat1, azimuth, color='orange', lw=2.0)
plt.show()
However, plotting the line is not enough, I want to be able to find the grid points that the great circle crosses so I can assign a value to them. Does anyone know how to go about this??
Can you specify which crossing point do you mean? Running your code returns only one line ...