Pcolormesh on basemap - python

I have a long list of latitudes and longitudes, and the ultimate goal is to plot the number of occurrences at each latitude/longitude pair on a map of North America. As an example, I have a DataFrame, called sort_df, that looks like:
longitude -95 -94 -93 -92
latitude
43 0 4 8 3
44 7 3 0 0
using sort_df.values and pmeshcolor, I can plot the data. The code is as follows:
m.Basemap(llcrnrlon = -170, llcrnrlat = 10, urcrnrlon = -50, urcrnrlat = 80)
m.drawcoastlines()
m.drawstates()
ny = sort_df.shape[0]
nx = sort_df.shape[1]
lons, lats = m.makegrid(nx,ny)
x,y = m(lons,lats)
my_cmap = plt.get_cmap('rainbow')
my_cmap.set_under('white')
data = np.array(sort_df.values)
cs = m.pcolormesh(x,y,data,cmap = my_cmap, vmin = 1)
m.colorbar(cs,extend = 'min')
plt.show()
The above code produces a plot that looks like:
The problem: There should only be colors on the lat/lon grid defined by sort_df.
Question: How do I make the grid appear in the correct spots on the map?

You made a missprint while convert lat-lon. Your arrays lats and lons are empty.
Another problem of your code is that data have to have shape of [nx-1, ny-1] to plot with pcolormesh (it draw between points):
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from pandas import DataFrame
m = Basemap(llcrnrlon = -170, llcrnrlat = 10, urcrnrlon = -50, urcrnrlat = 80)
m.drawcoastlines()
m.drawstates()
lon = np.array([-95, -94, -93, -92, -91])
lat = np.array([43, 42, 41])
data = np.array([[0,4,8,3],[7,3,0,0]])
# you have to write just like here to convert coordinates
x,y = m(lon,lat)
my_cmap = plt.get_cmap('rainbow')
my_cmap.set_under('white')
cs = m.pcolormesh(x,y,data,cmap = my_cmap)
m.colorbar(cs, extend = 'min')
plt.show()

Related

Python Basemap Heatmap

I am trying to copy the method that was done on this page: https://makersportal.com/blog/2018/7/20/geographic-mapping-from-a-csv-file-using-python-and-basemap under "Mapping Interesting Data" to have a color bar associated with my data.
Right now I just get a plain map of South America, which is what I want as my background but there is no data included.
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
m = Basemap(projection='mill',
llcrnrlat = -30, #bottom
llcrnrlon = -120, #left
urcrnrlat = 20, #top
urcrnrlon = -50, #right
resolution='c')
m.drawcoastlines()
m.drawcountries()
# format colors for elevation range
SST_min = np.min(df5.DaasgardSST)
SST_max = np.max(df5.DaasgardSST)
cmap = plt.get_cmap('gist_earth')
normalize = matplotlib.colors.Normalize(vmin=SST_min, vmax=SST_max)
# plot SST with different colors
for i in range(0,len(df5.DaasgardSST)):
x,y = m(lon,lat)
color_interp = np.interp(df5,[SST_min,SST_max],[0,30])
plt.plot(x,y,marker='o',markersize=6,color=cmap(int(color_interp)))
# format the colorbar
cax, _ = matplotlib.colorbar.make_axes(ax)
cbar = matplotlib.colorbar.ColorbarBase(cax, cmap=cmap,norm=normalize,label='Elevation')
plt.title('Title')
plt.show()

Fill oceans in basemap (don't plot colour grid on oceans)

My question is the same as this one: Fill oceans in basemap. I know there is an answer given here, but when I try to apply the answer to my code, it doesn't work.
Here is my original graph.
I want to make the oceans white and only show the colour differences on Antarctica.
Here is the code that I tried:
from mpl_toolkits.basemap import Basemap
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Path, PathPatch
D = pd.read_pickle('directory')
fig, ax = plt.subplots()
lons = D['longitude'][:]
lats = D['latitude'][:]
lon, lat = np.meshgrid(lons, lats)
variable_to_graph = D['variable_to_graph']
m = Basemap(projection='spstere', boundinglat=-60, lon_0=180, resolution='l')
m.drawcoastlines(color='k') # shows Antarctica
x,y = m(lon, lat)
ax.pcolormesh(x, y, variable_to_graph, cmap='viridis', vmin=-400, vmax=-0)
# 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()
Which gives me this.
I have also tried using a is_land() function but as the resolution is relatively low (1x1 degrees) the image is not very neat.
Any help would be appreciated as to how to get the colour map to only show on Antarctica and for it to stop on the edges of Antarctica.
EDIT: I have updated my question with code that can be tested.
from mpl_toolkits.basemap import Basemap
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Path, PathPatch
# D = pd.read_pickle('directory')
fig, ax = plt.subplots()
lons = range(0,361)
lats = [-60, -61, -62, -63, -64, -65, -66, -67, -68, -69, -70,
-71, -72, -73, -74, -75, -76, -77, -78, -79, -80, -81,
-82, -83, -84, -85, -86, -87, -88, -89, -90]
lon, lat = np.meshgrid(lons, lats)
variable_to_graph = np.random.rand(31,361)
m = Basemap(projection='spstere', boundinglat=-60, lon_0=180, resolution='l')
m.drawcoastlines(color='k') # shows Antarctica
x,y = m(lon, lat)
ax.pcolormesh(x, y, variable_to_graph, cmap='viridis', vmin=-400, vmax=-0)
# 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()

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:

All contours are not getting converted to shapefile in python

I am reading data from a csv file and plotting contours using python. The code works but when I check shapefile created, I can see only few contour lines are getting plotted whereas when I check image plot there are many more lines. What is going wrong in this code? I think the cs.collections is creating some problem but I am not able to figure it out.
import os
import numpy as np
import pandas as pd
from matplotlib.mlab import griddata
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
from shapely.geometry import mapping, Polygon, LineString,MultiLineString
import fiona
# set up plot
plt.clf()
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, axisbg='w', frame_on=False)
# grab data
data = pd.read_csv('Filename.csv', sep=',')
norm = Normalize()
# define map extent
lllon = -180
lllat = -90
urlon = 180
urlat = 90
# Set up Basemap instance
m = Basemap(#projection = 'merc',llcrnrlon = lllon, llcrnrlat = lllat, urcrnrlon = urlon, urcrnrlat = urlat,resolution='h', epsg=4326)
# transform lon / lat coordinates to map projection
data['projected_lon'], data['projected_lat'] = m(*(data.CLON.values, data.CLAT.values))
# grid data
numcols, numrows = 100, 100
xi = np.linspace(data['projected_lon'].min(), data['projected_lon'].max(), numcols)
yi = np.linspace(data['projected_lat'].min(), data['projected_lat'].max(), numrows)
xi, yi = np.meshgrid(xi, yi)
# interpolate
x, y, z = data['projected_lon'].values, data['projected_lat'].values, data.PRES.values
zi = griddata(x, y, z, xi, yi)
# contour plot
cs = plt.contour(xi, yi, zi,zorder=4)
plt.show()
lenvar = (len(cs.collections)) #number of contours
print(lenvar)
#print(cs.collections)
lines = [] # Empty list for contour sections
for i in range(lenvar):
n = i
p = cs.collections[i].get_paths()[0]
v = p.vertices # getting the individual verticies as a numpy.ndarray
x = v[:,0] #takes first column
y = v[:,1] #takes second column
line = LineString([(i[0], i[1]) for i in zip(x,y)]) #Defines Linestring
lines.append(line) #appends to list
schema = {'geometry': 'LineString','properties': {'id': 'int'}} # sets up parameter for shapefile
with fiona.open('ConShp.shp', 'w', 'ESRI Shapefile', schema) as c: # creates new file to be written to
for j in range(len(lines)):
l = (lines[j]) # creates variable
print(l)
print(type(l))
c.write({'geometry': mapping(l),'properties': {'id': j},})
data Sample is like:
Lat, Lon, Value
18.73, 26.34, 5000
20.00, 60.00, 7000
Shapefile_plot_in_GIS
Image of Plot in python

Shifting grid with matplotlib

I'm plotting data which is formatted between 0 and 360 degrees. I'm trying to plot this on cyl or merc projection, but it is only showing data from 0 onwards (I want to plot the data with the GMT in the center, so need the data on a lon grid of -180 to 180). If I shift the grid (lon = lon -180) then all data shows, but the data is in the wrong place by -180 degrees.
Issue:
Works fine in ortho projection though. Relevant code below.
lat = np.linspace(90,-90,721)
lon = np.linspace(0,360,1440)
m = Basemap(projection='cyl',llcrnrlat=-90,urcrnrlat=90,llcrnrlon=0,urcrnrlon=360,resolution='c',)
X, Y = np.meshgrid(lon, lat)
X, Y = m(X, Y)
cs = m.contourf(X,Y,Plot,scale, cmap=cmap)
Please Try:
import numpy as np
from mpl_toolkits.basemap import shiftgrid
from mpl_toolkits.basemap import Basemap
lat = np.linspace(-90, 90, 721)
lon = np.linspace(0, 360, 1440)
Plot, lon = shiftgrid(180., Plot, lon, start=False) # shiftgrid
m = Basemap(projection='cyl', llcrnrlat=-90, urcrnrlat=90, llcrnrlon=-180, urcrnrlon=180, resolution='c',)
X, Y = np.meshgrid(lon, lat)
X, Y = m(X, Y)
cs = m.contourf(X, Y, Plot, scale, cmap=cmap)
shiftgrid: shifts global lat/lon grids east or west.
I have a solution (albeit an ugly one). By reordering the data.
temp = np.zeros((721,1440))
temp[:,0:720] = Plot[:,720:1440]
temp[:,720:1440] = Plot[:,0:720]
Plot[:]=temp[:]
Or by using np.roll (if you know how many gridpoints to shift)

Categories

Resources