How to plot data only within Basemap country region - python

For the code below, besides having the relevant modules installed, you will need to download and unpack the file "nationp010g.shp.tar.gz" that can be found here. This file is a shape file of the United States. If anyone has a better way of displaying these boundaries, by all means suggest it!
For my test case below, I have succeeded in colouring in the united states blue. What I want to do is to plot contour plots as if only the land portions of the USA are allowed. Im not sure how this can be accomplished without using a tedious for loop which loops through all the coordinates and checks whether the coordinate in question is inside the polygon given by the shape file. What is the best way to accomplish what I want? Here is the result:
from __future__ import print_function
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap as Basemap
from matplotlib.colors import rgb2hex
from matplotlib.patches import Polygon
m = Basemap(llcrnrlon=-119,llcrnrlat=22,urcrnrlon=-64,urcrnrlat=49,
projection='lcc',lat_1=33,lat_2=45,lon_0=-95)
shp_info = m.readshapefile('nationp010g/nationp010g', 'borders', drawbounds=True)
print(dir(m)) #List all attributes of an object
ax = plt.gca()
color = 'blue'
for nshape,seg in enumerate(m.borders):
poly = Polygon(seg,facecolor=color,edgecolor=color)
ax.add_patch(poly)
xmax, ymax = m(m.lonmax, m.latmax )
xmin, ymin = m(m.lonmin, m.latmin)
y = np.linspace(ymin,ymax,100)
x = np.linspace(xmin, xmax, 100)
X, Y = np.meshgrid(x, y)
Z = (X-(xmax-xmin)/2)**2+(Y-(ymax-ymin)/2)**2
ax.contour(X,Y, Z, cmap=plt.get_cmap('coolwarm'))
plt.show()

Related

How to draw a 3D grid using matplotlib based on three columns of data?

I'm facing a problem with making a 3D plot. I want to build a 3D surface plot like below from three columns of data.
Expected graphic case
I have implemented a few currently, as shown below.
Current picture case
But I still don't know how to make it "grid" like the first picture? Does anyone know how to achieve this? Part of the code and full data are as follows.
import numpy as np
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
import os
import warnings
from mpl_toolkits.mplot3d import Axes3D
warnings.filterwarnings('ignore')
os.chdir(r"E:\SoftwareFile\stataFile")
matplotlib.use('TkAgg')
plt.figure(figsize=(10,6))
data = pd.read_stata(r"E:\SoftwareFile\stataFile\demo.dta")
ax = plt.axes(projection="3d")
ax.plot_trisurf(data["age"], data["weight"], data["pr_highbp"],
cmap=plt.cm.Spectral_r)
ax.set_xticks(np.arange(20, 90, step=10))
ax.set_yticks(np.arange(40, 200, step=40))
ax.set_zticks(np.arange( 0, 1.2, step=0.2))
ax.set_title("Probability of Hypertension by Age and Weight")
ax.set_xlabel("Age (years)")
ax.set_ylabel("Weight (kg")
ax.zaxis.set_rotate_label(False)
ax.set_zlabel("Probability of Hypertension", rotation=90)
ax.view_init(elev=30, azim=240)
plt.savefig("demo.png", dpi=1200)
Download all data
Sincerely appreciate your help
Remove the colormap and opacity in the trisurf command like so:
ax.plot_trisurf(
data["age"],
data["weight"],
data["pr_highbp"],
color=None,
linewidth=1,
antialiased=True,
edgecolor="Black",
alpha=0,
)
That should result in:
You could also take a look at plot_wireframe(). For that I think you have to start with
x = data["age"].to_list()
y = data["weight"].to_list()
X, Y = np.meshgrid(x, y)
But I'm not sure how to create the z coordinate. It seems you may need interpolation from what I read.

How to create legend with proxy artist for contourf plot in Matplotlib

I am trying to create a graphic where I overlay multiple contour plots on a single image. So I want to have colorbars for each of the plots, as well as a legend indicating what each contour represents. However Matplotlib will not allow me to create a separate legend for my contour plots. Simple example:
import matplotlib as mpl
import matplotlib.pyplot as plt
import cartopy
import cartopy.crs as ccrs
import numpy as np
def create_contour(i,j):
colors = ["red","green","blue"]
hatches = ['-','+','x','//','*']
fig = plt.figure()
ax = plt.axes(projection=ccrs.PlateCarree())
ax.set_extent((-15.0,15.0,-15.0,15.0))
delta = 0.25
x = np.arange(-3.0,3.0,delta)
y = np.arange(-2.0,2.0,delta)
X, Y = np.meshgrid(x, y)
data = np.full(np.shape(X), 1.0)
plot = ax.contourf(X,Y,data, levels = [float(i),float(i+1)], hatch=[hatches[j]], colors = colors[i], label="label")
plt.legend(handles=[plot], labels=["label"])
plt.savefig("figure_"+str(i)+".png")
create_contour(1,3)
When I run this, I get the following message:
UserWarning: Legend does not support
(matplotlib.contour.QuadContourSet object at 0x7fa69df7cac8)
instances. A proxy artist may be used instead. See:
http://matplotlib.org/users/legend_guide.html#creating-artists-specifically-for-adding-to-the-legend-aka-proxy-artists
"aka-proxy-artists".format(orig_handle)
But as far as I can tell, I am following those directions as closely as possible, the only difference being that they do not use contourf in the example.
Any help would be greatly appreciated.
The comments to your question look like they have solved the question (by making custom patches and passing those through to the legend). There is also an example that I added many years ago to the matplotlib documentation to do something similar (about the same time I added contour hatching to matplotlib): https://matplotlib.org/examples/pylab_examples/contourf_hatching.html#pylab-examples-contourf-hatching
It is such a reasonable request that there is even a method on the contour set to give you legend proxies out of the box: ContourSet.legend_elements.
So your example might look something like:
%matplotlib inline
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy as np
fig = plt.figure(figsize=(10, 10))
ax = plt.axes(projection=ccrs.PlateCarree())
ax.coastlines('10m')
y = np.linspace(40.0, 60.0, 30)
x = np.linspace(-10.0, 10.0, 40)
X, Y = np.meshgrid(x, y)
data = 2*np.cos(2*X**2/Y) - np.sin(Y**X)
cs = ax.contourf(X, Y, data, 3,
hatches=['//','+','x','o'],
alpha=0.5)
artists, labels = cs.legend_elements()
plt.legend(handles=artists, labels=labels)
plt.show()

How to plot odd even square wave using python

I am using the below python codes so as to generates a square wave at one specific position. For Eg: you enter 0, the signal is high1 only between 0 and 1[Odd=High]. You enter 1, the output is high1 only between 1 and 2 [Even = High]. How do you extend the below python codes so as to generate odd or even square wave throughout the time span rather that at a single position. Here I am facing problem with 2*n+1 formula.Could anyone help me in this?
Please refer the image below
import numpy as np
import matplotlib.pyplot as plt
def SquareWave(n):
xmin=0;
xmax=10;
ymin=-2;
ymax=2;
Nx=1000;
offset=1;
x=np.linspace(xmin, xmax, Nx);
y=np.sign(x+n)*offset;
y[(x<n)]=0;
y[(x>n+1)]=0;
plt.plot(x, y);
plt.axis([xmin, xmax, ymin, ymax]);
plt.grid()
plt.show()
Don't use ;.
import numpy as np
import matplotlib.pyplot as plt
def SquareWave(n,xmin=0,xmax=10,ymin=-2,Nx=1000,ymax=2,offset=1):
x=np.sort(np.concatenate([np.arange(xmin, xmax)-1E-6,np.arange(xmin, xmax)+1E-6]))
#You can use np.linspace(xmin,xmax,Nx) if you want the intermediate points
y=np.array(x+n+offset,dtype=int)%2
plt.plot(x, y)
plt.axis([xmin, xmax, ymin, ymax])
plt.grid()
plt.show()

How to use set clipped path for Basemap polygon

I want to use imshow (for example) to display some data inside the boundaries of a country (for the purposes of example I chose the USA) The simple example below illustrates what I want:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import RegularPolygon
data = np.arange(100).reshape(10, 10)
fig = plt.figure()
ax = fig.add_subplot(111)
im = ax.imshow(data)
poly = RegularPolygon([ 0.5, 0.5], 6, 0.4, fc='none',
ec='k', transform=ax.transAxes)
im.set_clip_path(poly)
ax.add_patch(poly)
ax.axis('off')
plt.show()
The result is:
Now I want to do this but instead of a simple polygon, I want to use the complex shape of the USA. I have created some example data contained in the array of "Z" as can be seen in the code below. It is this data that I want to display, using a colourmap but only within the boundaries of mainland USA.
So far I have tried the following. I get a shape file from here contained in "nationp010g.shp.tar.gz" and I use the Basemap module in python to plot the USA. Note that this is the only method I have found which gives me the ability get a polygon of the area I need. If there are alternative methods I would also be interested in them. I then create a polygon called "mainpoly" which is almost the polygon I want coloured in blue:
Notice how only one body has been coloured, all other disjoint polygons remain white:
So the area coloured blue is almost what I want, note that there are unwanted borderlines near canada because the border actually goes through some lakes, but that is a minor problem. The real problem is, why doesn't my imshow data display inside the USA? Comparing my first and second example codes I can't see why I don't get a clipped imshow in my second example, the way I do in the first. Any help would be appreciated in understanding what I am missing.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap as Basemap
from matplotlib.patches import Polygon
# Lambert Conformal map of lower 48 states.
m = Basemap(llcrnrlon=-119,llcrnrlat=22,urcrnrlon=-64,urcrnrlat=49,
projection='lcc',lat_1=33,lat_2=45,lon_0=-95)
shp_info = m.readshapefile('nationp010g/nationp010g', 'borders', drawbounds=True) # draw country boundaries.
for nshape,seg in enumerate(m.borders):
if nshape == 1873: #This nshape denotes the large continental body of the USA, which we want
mainseg = seg
mainpoly = Polygon(mainseg,facecolor='blue',edgecolor='k')
nx, ny = 10, 10
lons, lats = m.makegrid(nx, ny) # get lat/lons of ny by nx evenly space grid.
x, y = m(lons, lats) # compute map proj coordinates.
Z = np.zeros((nx,ny))
Z[:] = np.NAN
for i in np.arange(len(x)):
for j in np.arange(len(y)):
Z[i,j] = x[0,i]
ax = plt.gca()
im = ax.imshow(Z, cmap = plt.get_cmap('coolwarm') )
im.set_clip_path(mainpoly)
ax.add_patch(mainpoly)
plt.show()
Update
I realise that the line
ax.add_patch(mainpoly)
does not even add the polygon shape to a plot. Am I not using it correctly? As far as I know mainpoly was calculated correctly using the Polygon() method. I checked that the coordinate inputs are a sensible:
plt.plot(mainseg[:,0], mainseg[:,1] ,'.')
which gives
I have also considered about this problem for so long.
And I found NCL language has the function to mask the data outside some border.
Here is the example:
http://i5.tietuku.com/bdb1a6c007b82645.png
The contourf plot only show within China border. Click here for the code.
I know python has a package called PyNCL which support all NCL code in Python framework.
But I really want to plot this kind of figure using basemap. If you have figured it out, please post on the internet. I'll learn at the first time.
Thanks!
Add 2016-01-16
In a way, I have figured it out.
This is my idea and code, and it's inspired from this question I have asked today.
My method:
1. Make the shapefile of the interesting area(like U.S) into shapely.polygon.
2. Test each value point within/out of the polygon.
3. If the value point is out of the study area, mask it as np.nan
Intro
* the polygon xxx was a city in China in ESRI shapefile format.
* fiona, shapely package were used here.
# generate the shapely.polygon
shape = fiona.open("xxx.shp")
pol = shape.next()
geom = shape(pol['geometry'])
poly_data = pol["geometry"]["coordinates"][0]
poly = Polygon(poly_data)
It shows like:
http://i4.tietuku.com/2012307faec02634.png
### test the value point
### generate the grid network which represented by the grid midpoints.
lon_med = np.linspace((xi[0:2].mean()),(xi[-2:].mean()),len(x_grid))
lat_med = np.linspace((yi[0:2].mean()),(yi[-2:].mean()),len(y_grid))
value_test_mean = dsu.mean(axis = 0)
value_mask = np.zeros(len(lon_med)*len(lat_med)).reshape(len(lat_med),len(lon_med))
for i in range(0,len(lat_med),1):
for j in range(0,len(lon_med),1):
points = np.array([lon_med[j],lat_med[i]])
mask = np.array([poly.contains(Point(points[0], points[1]))])
if mask == False:
value_mask[i,j] = np.nan
if mask == True:
value_mask[i,j] = value_test_mean[i,j]
# Mask the np.nan value
Z_mask = np.ma.masked_where(np.isnan(so2_mask),so2_mask)
# plot!
fig=plt.figure(figsize=(6,4))
ax=plt.subplot()
map = Basemap(llcrnrlon=x_map1,llcrnrlat=y_map1,urcrnrlon=x_map2,urcrnrlat=y_map2)
map.drawparallels(np.arange(y_map1+0.1035,y_map2,0.2),labels= [1,0,0,1],size=14,linewidth=0,color= '#FFFFFF')
lon_grid = np.linspace(x_map1,x_map2,len(x_grid))
lat_grid = np.linspace(y_map1,y_map2,len(y_grid))
xx,yy = np.meshgrid(lon_grid,lat_grid)
pcol =plt.pcolor(xx,yy,Z_mask,cmap = plt.cm.Spectral_r ,alpha =0.75,zorder =2)
result
http://i4.tietuku.com/c6620c5b6730a5f0.png
http://i4.tietuku.com/a22ad484fee627b9.png
original result
http://i4.tietuku.com/011584fbc36222c9.png

Orthographic projection Python

I use orthographic projection to plot maps.
I use this programm:
from mpl_toolkits.basemap import Basemap
import numpy as np
import matplotlib.pyplot as plt
import os, sys
from sys import argv
import pylab
from mpl_toolkits.basemap import Basemap, shiftgrid
from matplotlib import mpl
from matplotlib import rcParams
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import matplotlib.patches as patches
import matplotlib.path as path
import matplotlib.dates as dt
from numpy import linalg
import netCDF4
import time
import datetime as d
import sys
import math
from mpl_toolkits.axes_grid1 import make_axes_locatable
from pylab import *
nc = netCDF4.Dataset ('tt.nc')
latvar = nc.variables['lat']
lat = latvar[:]
lon = nc.variables['lon'][:]
lat_0=30;lon_0=-25
m1 = Basemap(projection='ortho',lon_0=-25,lat_0=30,resolution='l')
m = Basemap(projection='ortho',lon_0=lon_0,lat_0=lat_0,resolution='l',\
llcrnrx=0.,llcrnry=0.,urcrnrx=m1.urcrnrx/2.,urcrnry=m1.urcrnry/2.)
X, Y = m(lon, lat)
O_x_1=nc.variables['O3']
h=9
lev=0
minOzone=0
maxOzone=40
plotOzone = m.pcolor(X,Y,O_x_1[h,lev,:,:],vmin=minOzone,vmax=maxOzone)
ax=colorbar(plotOzone, shrink=0.8,norm=(0,40))
m.drawcoastlines()
m.drawparallels(np.arange(-90.,120.,30.))
m.drawmeridians(np.arange(0.,420.,60.))
plt.show()
What do I have to do to center my map
on Europe ?
I have already played with lat_0 and lon_0 but that doesn't
give what I want...
I can't add figures to show what I obtained and
what I would like...
Thank you!
The lat_0 and lon_0 are for setting the origin of the projection, not the extent of the map. Usually the place with the least distortions so you dont want the origin to deviate too much from the center of your area of interest. Basemap will automatically center the map around the origin if you dont specify an extent.
Centering the map (different from its origin) can be done if you know what extent (or boundingbox) you want to use. If you know the corner coordinates in your 'ortho' projection you could use the keywords from your example (llcrnrx etc.). I have had no luck with the 'llcrnrlon' keywords in Basemap 1.0.6, they seem to suggest that you can input the coordinates of your extent in geographic (lat/lon).
An alternative is to grab the axes and manually set an x- and y limit. A benefit is that you can do it after declaring the Basemap object which you can then use for coordinate transformation. An example:
from mpl_toolkits.basemap import Basemap
fig = plt.figure(figsize=(5,5))
m = Basemap(projection='ortho',lon_0=5,lat_0=35,resolution='l')
m.drawcoastlines()
m.drawparallels(np.arange(-90.,120.,15.))
m.drawmeridians(np.arange(0.,420.,30.))
# your extent in lat/lon (dec degrees)
ulx = -10
uly = 65
lrx = 65
lry = 35
# transform coordinates to map projection
xmin, ymin = m(ulx, lry)
xmax, ymax = m(lrx, uly)
# set the axes limits
ax = plt.gca()
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
Make sure the projection in the map declaration suites your needs, i have just picked an origin which falls within Europe.

Categories

Resources