Overlay coastlines on a matplotlib plot - python

I'm looking to overlay some coastlines on graph representing an area. The area is defined by the box:
Top: 3900000
Bottom: 3450000
Left: 300000
Right: 800000
with the coordinate system WGS_1984_UTM_Zone_36N.
I've tried using mpl_toolkits.basemap however I can't work out how to specify that area as the ESPG code (32636) is not accepted by Basemap, and when I attempt to manually insert the projection parameters (m = Basemap(projection='tmerc', k_0=0.9996, lat_0=0, lon_0=33, llcrnrx=300000, llcrnry=3450000, urcrnrx=800000, urcrnry=3900000) it still requires a lat long boundary box.
Is there a another way to define that area in Basemap?
Thanks!
Edit: I'm trying to return an area of coastline defined by a box that is in the utm system, using lat/long values for the extremities of the box would result in over/underlap of the area covered by the coastlines when converted back into the utm system (I think, please correct me if I'm wrong).

Try cartopy and its new epsg feature:
projection = ccrs.epsg(32636)
fig, ax = plt.subplots(figsize=(5, 5),
subplot_kw=dict(projection=projection))
ax.coastlines(resolution='10m')
Here is a notebook with an example:
http://nbviewer.ipython.org/gist/ocefpaf/832cf7917c21da229564

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
# make sure the value of resolution is a lowercase L,
# for 'low', not a numeral 1
map = Basemap(projection='merc', lat_0=57, lon_0=-135,
resolution = 'h', area_thresh = 0.1,
llcrnrlon=-136.25, llcrnrlat=56,
urcrnrlon=-134.25, urcrnrlat=57.75)
map.drawcoastlines()
map.drawcountries()
map.fillcontinents(color='coral')
map.drawmapboundary()
map.drawmeridians(np.arange(0, 360, 30))
map.drawparallels(np.arange(-90, 90, 30))
plt.show()
All at this link https://peak5390.wordpress.com/2012/12/08/matplotlib-basemap-tutorial-making-a-simple-map/

Related

Python: Delaunay triangle vertices don't "line up" when saved as .pdf or .png

I created a set of Delaunay triangles using scipy.spatial.
from matplotlib import pyplot as plt
import matplotlib
from skimage import io
from scipy.spatial import Delaunay
import numpy as np
from PIL import Image
from scipy.ndimage import rotate
h = 700
w = 700
npts = 500
pts = np.zeros((npts,2))
pts[:,0] = np.random.randint(0,w,npts)
pts[:,1] = np.random.randint(0,h,npts)
tri = Delaunay(pts)
centers = np.sum(pts[tri.simplices], axis=1, dtype='int')/3.0
#plt.figure()
fig, ax = plt.subplots()
plt.xlim(0, w)
plt.ylim(0, h)
for i in range(0,len(pts[tri.simplices])-1):
temp_tri = plt.Polygon(pts[tri.simplices][i], color = colors[i]/256) #colors variable is a numpy.ndarray variable that contains RGB values
plt.gca().add_patch(temp_tri)
plt.gca().set_aspect('equal')
plt.axis('off')
plt.savefig('test.pdf', bbox_inches = 'tight', dpi=fig.dpi)
plt.show()
On the screen, the output is as I intended. However, when I save it as pdf or png, the vertices of Delaunay triangles do not match (they do when I look at them through plt.show())
The picture below is part of the whole picture, just to highlight where the vertices don't match.
Few suggestions I found with issues regarding different images shown with plt.show() and fitsave() said I should match the dpi, which I have done.
Please advise on what I should try. Thank you so much in advance!
I can't tell the reason why it fails to produce the correct points for the triangles, but in general it's probably better to create a PolyCollection from the vertices, instead of individual triangles.
In that case the problem would not appear.
pc = PolyCollection(pts[tri.simplices],
facecolors=np.random.rand(len(pts[tri.simplices]),3),
edgecolor="face",linewidth=0.1)
plt.gca().add_collection(pc)
Here some remaining overlap is seen, which is due to the linewidth. Smaller linewidths would look better in the pdf, e.g. linewidth=0.01, but might result in "white" lines in the graphics on screen. You may play around with that parameter until you are happy with the result.

How to define map width/height using Matplot Basemap on Jupyter Notebook?

TL; DR
How to I set the map to be exactly 1600x900 px?
Description
I am trying to draw a map with Jupyter Notebook using Basemap library as follows:
from mpl_toolkits.basemap import Basemap
import numpy as np
import matplotlib.pyplot as plt
atlas = Basemap(
llcrnrlon = -10.5, # Longitude lower right corner
llcrnrlat = 35, # Latitude lower right corner
urcrnrlon = 14.0, # Longitude upper right corner
urcrnrlat = 44.0, # Latitude upper right corner
resolution = 'i', # Crude resolution
projection = 'tmerc', # Transverse Mercator projection
lat_0 = 39.5, # Central latitude
lon_0 = -3.25 # Central longitude
)
atlas.drawmapboundary(fill_color='aqua')
atlas.fillcontinents(color='#cc9955',lake_color='aqua')
atlas.drawcoastlines()
plt.show()
and getting the following result
Is it possible to make the drawn map larger, defining the minimum width and height it should have?
You can use figure.
For example:
plt.figure(figsize=(1, 1))
Creates an inch-by-inch image, which will be 80-by-80 pixels unless you also give a different dpi argument.
You can change dpi with two possible ways
Way 1: Passing it as an argument to the figure as the following:(i.e 300)
plt.figure(figsize=(1, 1),dpi=300)
Way 2:Passing it to savefig()
plt.savefig("foo.png", dpi=300)
Perfect Example

Python Matplotlib Basemap - how to set zoom level

I have a dataframe with locations given as longitude and latitude coordinates (in degrees). Those locations are around New York. Therefore I setup a Basemap in Python that nicely shows all those locations. Works fine!
But: the map is drawn inline and it's very tiny. How can I force that figure to be let's say 3 times larger (zoom=3).
Here's the code. The data is from the Kaggle Two Sigma Rental Listing challenge.
%matplotlib inline
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
# New York Central Park
# Longitude: -73.968285
# Latitude: 40.785091
m = Basemap(projection='merc',llcrnrlat=40,urcrnrlat=42,\
llcrnrlon=-75, urcrnrlon=-72, resolution='i', area_thresh=50, lat_0=40.78, lon_0=-73.96)
m.drawmapboundary()
m.drawcoastlines(color='black', linewidth=0.4)
m.drawrivers(color='blue')
m.fillcontinents(color='lightgray')
lons = df['longitude'].values
lats = df['latitude'].values
x,y = m(lons, lats)
# r = red; o = circle marker (see: http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.plot)
m.plot(x, y, 'ro', markersize=4)
plt.show()
normally it would be as simple as:
plt.figure(figsize=(20,10))
How do you change the size of figures drawn with matplotlib?
but there are some other options too, see:
How to maximize a plt.show() window using Python
also to get the current size (for the purpose of "zoom")
How to get matplotlib figure size
regarding the specific issue:
the figure is inline inside a Jupyter notebook
before creating or plotting the map/figure:
import matplotlib
matplotlib.rcParams['figure.figsize'] = (30,30)

Matplotlib Mollweide/Hammer projection: region of interest only

I was wondering if there's a way to only show the region of interest from a plot based on Mollweide/Hammer projection in basemap (matplotlib).
I am trying to set the plot-bounds roughly to the Pacific plate, as in the link below. However, the set_xlim and set_ylim functions do not seem to have any effect. Thanks in advance for any guidance.
http://geology.gsapubs.org/content/29/8/695/F1.large.jpg
From the documentation, both Hammer and Mollweide projections don't allow this as they print out entire world maps. Here's some code using Polyconic projection, but it is bounded by straight lines. The trick here is to define the corner longitude and latitudes on creation.
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
my_map = Basemap(projection='poly', lat_0=0, lon_0=-160,
resolution = 'h', area_thresh = 0.1,
llcrnrlon=140, llcrnrlat=-60,
urcrnrlon=-100, urcrnrlat=60)
plt.figure(figsize=(16,12))
my_map.drawcoastlines()
my_map.drawcountries()
my_map.fillcontinents(color='coral', lake_color='aqua')
my_map.drawmapboundary(fill_color='aqua')
my_map.drawmeridians(np.arange(0, 360, 20))
my_map.drawparallels(np.arange(-90, 90, 10))
plt.show()
Result:
Note that this effectively shows less area than the one in the picture you provided.

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

Categories

Resources