drawing polygon onto basemap - python

There are many similar questions to this (How to draw rectangles on a Basemap , and http://matplotlib.1069221.n5.nabble.com/display-a-filled-lat-lon-basemap-rectangle-td11562.html) but I still cannot figure out how to do this.
I want to shade an area covered by two boxes, and the coordinates of each corner (UR = upper right, LL = lower left..) are given by :
box 1:
UR_box1_lat = 72.9
UR_box1_lon = -160
LL_box1_lat = 71.2
LL_box1_lon = -176.5
box 2 :
UL_box2_lat = LL_box1_lat
UL_box2_lon = LL_box1_lon
LR_box2_lat = 69.304
LR_box2_lon = -164.5
This produces my underlying map of the domain where I want to shade my polygons on top of:
# make map with these (i_total, j_total) indices as a box shaded or outlined..
# read in etopo5 topography/bathymetry.
url = 'http://ferret.pmel.noaa.gov/thredds/dodsC/data/PMEL/etopo5.nc'
etopodata = Dataset(url)
topoin = etopodata.variables['ROSE'][:]
lons = etopodata.variables['ETOPO05_X'][:]
lats = etopodata.variables['ETOPO05_Y'][:]
# shift data so lons go from -180 to 180 instead of 20 to 380.
topoin,lons = shiftgrid(180.,topoin,lons,start=False)
# plot topography/bathymetry as an image.
# create the figure and axes instances.
fig = plt.figure()
ax = fig.add_axes([0.1,0.1,0.8,0.8])
# setup of basemap ('lcc' = lambert conformal conic).
# use major and minor sphere radii from WGS84 ellipsoid.
m = Basemap(llcrnrlon=175.,llcrnrlat=50.,urcrnrlon=-120.,urcrnrlat=75.,\
rsphere=(6378137.00,6356752.3142),\
resolution='l',area_thresh=1000.,projection='lcc',\
lat_1=66.,lon_0=-169.,ax=ax)
# transform to nx x ny regularly spaced 5km native projection grid
nx = int((m.xmax-m.xmin)/5000.)+1; ny = int((m.ymax-m.ymin)/5000.)+1
topodat = m.transform_scalar(topoin,lons,lats,nx,ny)
# plot image over map with imshow.
im = m.imshow(topodat,cm.GMT_haxby)
# draw coastlines and political boundaries.
m.drawcoastlines()
m.drawcountries()
m.drawstates()
# draw parallels and meridians.
# label on left and bottom of map.
parallels = np.arange(0.,80,15.)
m.drawparallels(parallels,labels=[1,0,0,0])
#m.drawparallels(np.array([50.,70]))
meridians = np.arange(10.,360.,30.)
m.drawmeridians(meridians,labels=[1,0,0,1])
# add colorbar
cb = m.colorbar(im,"right", size="5%", pad='2%')
I have tried (but this is only an attempt at the top box, but I want the entire area shaded of both boxes):
def draw_screen_poly( lats, lons, m):
x, y = m( lons, lats )
xy = zip(x,y)
poly = Polygon( xy, facecolor='red', alpha=0.4 )
plt.gca().add_patch(poly)
lats_box1 = np.linspace(71.2, 72.9, num=25)
lons_box1 = np.linspace(-176.5, -160, num=25)
#lats_box1 = [71.2,72.9,72.9,71.2]
#lons_box1 = [-160,-160,-176.5,-176.5]
#m = Basemap(projection='sinu',lon_0=0)
#m.drawcoastlines()
#m.drawmapboundary()
draw_screen_poly( lats_box1, lons_box1, m )
and also (this is also an attempt at just the top box):
x1,y1 = m(71.2,-176.5)
x2,y2 = m(72.9,-176.5)
x3,y3 = m(72.9,-160)
x4,y4 = m(71.2,-160)
p = Polygon([(x1,y1),(x2,y2),(x3,y3),(x4,y4)],facecolor='red',edgecolor='blue',linewidth=2)
plt.gca().add_patch(p)
plt.show()
Any help is greatly appreciated, I have been stuck on this for quite some time

Related

Plotting a parameter along the depth in 3D in Python

I have to visualize the length of cylinder in 3d along with some other parameters. I am able to plot a 3d cylinder in python using matplotlib and also define color for range of points. How I can draw a line through the middle of the cylinder and also plot other parameters like angles at that interval, varying radius etc.
The main idea is to have a well like structure and display some measurements along the depth of the cylinder.
Thanks in advance!
Code used to generate the cylinder:
fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')
u = np.linspace(0,2*np.pi) # divide circle into parts
#h = np.linspace(0,100,6) # divide height into parts
h = np.arange(3560.064,4213.86,step)
x = np.outer(np.sin(u), np.ones(len(h)))
y = np.outer(np.cos(u), np.ones(len(h)))
z = np.outer(np.ones(len(u)), h)
bounds = [3500,3650,4200,4214]
colors = ["blue", "red","green"]
cmap = matplotlib.colors.ListedColormap(colors)
norm = matplotlib.colors.BoundaryNorm(bounds, len(colors))
ax.plot_surface(x,y,z, cmap = cmap, norm = norm)
plt.show()
Generated Cylinder

Plotting data on map on python

When I try to display the data of the population nothing is shown as output but when I comment the code to display population everything works fine so what is the problem in code?
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.cm as cm
plt.figure(figsize=(25,15))
# lon_0 is central longitude of robinson projection.
# resolution = 'c' means use crude resolution coastlines.
m=Basemap(projection='mill',llcrnrlat=5,urcrnrlat=40,\
llcrnrlon=65,urcrnrlon=95,resolution='i')
#set a background colour
m.drawmapboundary(fill_color='#85A6D9')
# draw coastlines, country boundaries, fill continents.
m.fillcontinents(color='white',lake_color='#85A6D9')
m.drawcoastlines(color='#6D5F47', linewidth=.4)
m.drawcountries(color='#6D5F47', linewidth=.4)
# draw lat/lng grid lines every 30 degrees.
m.drawmeridians(np.arange(-180, 180, 5), color='#bbbbbb')
m.drawparallels(np.arange(-90, 90, 5), color='#bbbbbb')
# lat/lon coordinates of states
lats = [16.00,28.00,26.24,25.11,21.29513,28.38000,28.38000,22.30943,29.23848,32.08421,32.44000,23.45000,15.31728,10.85052,23.47332,19.60119,24.44000,25.30000,23.30000,26.00000,20.94092,30.40000,27.39128,27.30000,11.12712,23.74513,28.20761,30.15000,22.97862]
lngs = [80.00,95.00,92.53,85.32000,81.82823,77.12000,72.12000,72.13623,76.43189,77.57117,74.54000,85.30000,75.71389,76.27108,77.94800,75.55298,93.58000,91.00000,20.52000,94.20000,84.80347,75.50000,73.43262,88.30000,78.65689,91.74683,79.82666,79.15000,87.74780]
populations = [84.58,1.38,31.20,104.099452,25.545198,16.787941,1.458545,60.439692,25.351462,6.864602,12.541302,32.988134,61.095297,33.406061,72.626809,112.374333,2.855794,2.966889,1.097206,1.978502,41.974218,27.743338,68.548437,0.610577,72.14703,3.673917,199.812341,10.086292,91.276115] #millions
# compute the native map projection coordinates for cities
x,y = m(lngs,lats)
#scale populations to emphasise different relative pop sizes
s_populations = [p * p for p in populations]
#defining color
s_colors= [cm.rainbow for p in populations]
#scatter scaled circles at the city locations
m.scatter(
x,
y,
s=s_populations, #size
c='darkblue', #color
marker='o', #symbol
alpha=0.3, #transparency
zorder = 5, #plotting order
)
## plot population labels.
#for population, xpt, ypt in zip(populations, x, y):
# label_txt = int(round(population, 0)) #round to 0 dp and display as integer
# plt.text(
# xpt,
# ypt,
# label_txt,
# color = 'black',
# size='small',
# horizontalalignment='center',
# verticalalignment='center',
# zorder =3 ,
# )
#add a title and display the map on screen
plt.title('Population according to state')
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:

Plotting shapefile using LineCollection shows all edges, but partially fills them

For the past few days I have been trying to get weather station data interpolated in a map for my country only. I do this as follows:
Loading the data, I create a grid using interpolation
Based on this grid I draw a contour and contourf image
I then draw shapefiles for Germany, Belgium and France on top of the map in order to cover the irrelevant contour/contourf elements. I used this tutorial for that.
Finally, I use an oceans shapefile (IHO Sea Areas; www.marineregions.org/downloads.php#iho) to plot as well in order to cover the North Sea. Using QGIS, I edited this oceans shapefile and removed everything except for the North Sea - given time constraints :)
You would say that everything goes fine -- but for some reason parts of the country as well as the islands are interpreted as being water. I guess this is because these are distinct parts, all not connected to the main land (due to waters/rivers).
Strangely enough, the edges are drawn, but they are not filled.
I've tried and searched a lot but have no clue about how to fix this. I guess it is somewhere in the LineCollection because in QGIS the shapefile is correct (i.e. it recognizes no shape when clicking the islands etc., which is correct since it should only recognize a shape when clicking on the sea).
I sincerely hope that you could help me point out where I'm wrong and how I can fix this!
Thanks a lot!
This is the map that I'm getting:
https://i.imgur.com/GHISN7n.png
My code is as follows (and you can probably see that I'm very new to this kind of programming, I started yesterday :)):
import numpy as np
import matplotlib
matplotlib.use('Agg')
from scipy.interpolate import griddata
from mpl_toolkits.basemap import Basemap, maskoceans
import matplotlib.pyplot as plt
from numpy.random import seed
import shapefile as shp
from matplotlib.collections import LineCollection
from matplotlib import cm
# Set figure size
plt.figure(figsize=(15,15), dpi=80)
# Define map bounds
xMin, xMax = 2.5, 8.0
yMin, yMax = 50.6, 53.8
# Create map
m = Basemap(projection='merc',llcrnrlon=xMin,llcrnrlat=yMin,urcrnrlon=xMax,urcrnrlat=yMax,resolution='h')
m.drawmapboundary(fill_color='#d4dadc',linewidth=0.25)
# m.drawcoastlines(linewidth=0.5,color='#333333')
# Load data
y = [54.325666666667,52.36,53.269444444444,55.399166666667,54.116666666667,53.614444444444,53.491666666667,53.824130555556,52.918055555556,54.03694,52.139722,52.926865008825,54.853888888889,52.317222,53.240026656696,52.642696895243,53.391265948394,52.505333893732,52.098821802977,52.896643913235,52.457270486008,53.223000488316,52.701902388132,52.0548617826,53.411581103636,52.434561756559,52.749056395511,53.123676213651,52.067534268959,53.194409573306,52.27314817052,51.441334059998,51.224757511326,51.990941918858,51.447744494043,51.960667359998,51.969031121385,51.564889021961,51.857593837453,51.449772459909,51.658528382201,51.196699902606,50.905256257898,51.497306260089,yMin,yMin,yMax,yMax]
x = [2.93575,3.3416666666667,3.6277777777778,3.8102777777778,4.0122222222222,4.9602777777778,5.9416666666667,2.9452777777778,4.1502777777778,6.04167,4.436389,4.7811453228565,4.6961111111111,4.789722,4.9207907082729,4.9787572406902,5.3458010937365,4.6029300588208,5.1797058644882,5.383478899702,5.5196324030324,5.7515738887123,5.8874461671401,5.8723225499118,6.1990994508938,6.2589770334531,6.5729701105864,6.5848470019087,6.6567253619722,7.1493220605216,6.8908745111116,3.5958241584686,3.8609657214986,4.121849767852,4.342014,4.4469005114756,4.9259216999194,4.9352386335384,5.1453989235756,5.3770039280214,5.7065946674719,5.7625447234516,5.7617834850481,6.1961067840608,xMin,xMax,xMin,xMax]
z = [4.8,5.2,5.8,5.4,5,5.3,5.4,4.6,5.8,6.3,4.8,5.4,5.3,4.6,5.4,4.4,4.1,5.5,4.5,4.2,3.9,3.7,4.2,3.2,4,3.8,2.7,2.3,3.4,2.5,3.7,5.2,2.9,5.1,3.8,4.4,4.2,3.9,3.8,3.2,2.6,2.8,2.4,3.1]
avg = np.average(z)
z.extend([avg,avg,avg,avg])
x,y = m(x,y)
# target grid to interpolate to
xis = np.arange(min(x),max(x),2000)
yis = np.arange(min(y),max(y),2000)
xi,yi = np.meshgrid(xis,yis)
# interpolate
zi = griddata((x,y),z,(xi,yi),method='cubic')
# Decide on proper values for colour bar (todo)
vrange = max(z)-min(z)
mult = 2
vmin = min(z)-(mult*vrange)
vmax = max(z)+(mult*vrange)
# Draw contours
cs = m.contour(xi, yi, zi, 5, linewidths=0.25, colors='k')
cs = m.contourf(xi, yi, zi, 5,vmax=vmax,vmin=vmin,cmap=plt.get_cmap('jet'))
# Plot seas from shapefile
sf = shp.Reader(r'/DIR_TO_SHP/shapefiles/northsea')
shapes = sf.shapes()
print shapes[0].parts
records = sf.records()
ax = plt.subplot(111)
for record, shape in zip(records,shapes):
lons,lats = zip(*shape.points)
data = np.array(m(lons, lats)).T
print len(shape.parts)
if len(shape.parts) == 1:
segs = [data,]
else:
segs = []
for i in range(1,len(shape.parts)):
index = shape.parts[i-1]
index2 = shape.parts[i]
segs.append(data[index:index2])
segs.append(data[index2:])
lines = LineCollection(segs,antialiaseds=(1,),zorder=3)
lines.set_facecolors('#abc0d3')
lines.set_edgecolors('red')
lines.set_linewidth(0.5)
ax.add_collection(lines)
# Plot France from shapefile
sf = shp.Reader(r'/DIR_TO_SHP/shapefiles/FRA_adm0')
shapes = sf.shapes()
records = sf.records()
ax = plt.subplot(111)
for record, shape in zip(records,shapes):
lons,lats = zip(*shape.points)
data = np.array(m(lons, lats)).T
if len(shape.parts) == 1:
segs = [data,]
else:
segs = []
for i in range(1,len(shape.parts)):
index = shape.parts[i-1]
index2 = shape.parts[i]
segs.append(data[index:index2])
segs.append(data[index2:])
lines = LineCollection(segs,antialiaseds=(1,))
lines.set_facecolors('#fafaf8')
lines.set_edgecolors('#333333')
lines.set_linewidth(0.5)
ax.add_collection(lines)
# Plot Belgium from shapefile
sf = shp.Reader(r'/DIR_TO_SHP/shapefiles/BEL_adm0')
shapes = sf.shapes()
records = sf.records()
ax = plt.subplot(111)
for record, shape in zip(records,shapes):
lons,lats = zip(*shape.points)
data = np.array(m(lons, lats)).T
if len(shape.parts) == 1:
segs = [data,]
else:
segs = []
for i in range(1,len(shape.parts)):
index = shape.parts[i-1]
index2 = shape.parts[i]
segs.append(data[index:index2])
segs.append(data[index2:])
lines = LineCollection(segs,antialiaseds=(1,))
lines.set_facecolors('#fafaf8')
lines.set_edgecolors('#333333')
lines.set_linewidth(0.5)
ax.add_collection(lines)
# Plot Germany from shapefile
sf = shp.Reader(r'/DIR_TO_SHP/shapefiles/DEU_adm0')
shapes = sf.shapes()
records = sf.records()
ax = plt.subplot(111)
for record, shape in zip(records,shapes):
lons,lats = zip(*shape.points)
data = np.array(m(lons, lats)).T
if len(shape.parts) == 1:
segs = [data,]
else:
segs = []
for i in range(1,len(shape.parts)):
index = shape.parts[i-1]
index2 = shape.parts[i]
segs.append(data[index:index2])
segs.append(data[index2:])
lines = LineCollection(segs,antialiaseds=(1,))
lines.set_facecolors('#fafaf8')
lines.set_edgecolors('#333333')
lines.set_linewidth(0.5)
ax.add_collection(lines)
# Finish plot
plt.axis('off')
plt.savefig("test2.png",bbox_inches='tight',pad_inches=0);
Your problem is that LineCollection does not do what you think it does. What you want is an outer filled shape with 'holes punched in' that have the shapes of the other lines in your LineCollection (i.e. the islands in the north sea). LineCollection, however, fills every line segment in the list, so the filled shapes just overlay each other.
Inspired by this post, I wrote an answer that seems to solve your problem using Patches. However, I'm not entirely sure how robust the solution is: according to the linked (unanswered) post, the vertices of the outer shape should be ordered clockwise while the vertices of the 'punched in' holes should be ordered counter-clock wise (I checked that too and it appears to be correct; this matplotlib example shows the same behaviour). As the shapefiles are binary, it is hard to verify the ordering of the vertices, but the result appears to be correct. In the example below I assume that in each shapefile the longest line segment is the outline of the country (or north sea), while shorter segments are islands or some such. I thus first order the segments of each shapefile by length and create a Path and a PathPatch thereafter. I took the freedom to use a different colour for each shapefile in order to make sure that everything works as it should.
import numpy as np
import matplotlib
matplotlib.use('Agg')
from scipy.interpolate import griddata
from mpl_toolkits.basemap import Basemap, maskoceans
import matplotlib.pyplot as plt
from numpy.random import seed
import shapefile as shp
from matplotlib.collections import LineCollection
from matplotlib.patches import Path, PathPatch
from matplotlib import cm
# Set figure size
fig, ax = plt.subplots(figsize=(15,15), dpi = 80)
# Define map bounds
xMin, xMax = 2.5, 8.0
yMin, yMax = 50.6, 53.8
shapefiles = [
'shapefiles/BEL_adm0',
'shapefiles/FRA_adm0',
'shapefiles/DEU_adm0',
'shapefiles/northsea',
]
colors = ['red', 'green', 'yellow', 'blue']
y = [54.325666666667,52.36,53.269444444444,55.399166666667,54.116666666667,53.614444444444,53.491666666667,53.824130555556,52.918055555556,54.03694,52.139722,52.926865008825,54.853888888889,52.317222,53.240026656696,52.642696895243,53.391265948394,52.505333893732,52.098821802977,52.896643913235,52.457270486008,53.223000488316,52.701902388132,52.0548617826,53.411581103636,52.434561756559,52.749056395511,53.123676213651,52.067534268959,53.194409573306,52.27314817052,51.441334059998,51.224757511326,51.990941918858,51.447744494043,51.960667359998,51.969031121385,51.564889021961,51.857593837453,51.449772459909,51.658528382201,51.196699902606,50.905256257898,51.497306260089,yMin,yMin,yMax,yMax]
x = [2.93575,3.3416666666667,3.6277777777778,3.8102777777778,4.0122222222222,4.9602777777778,5.9416666666667,2.9452777777778,4.1502777777778,6.04167,4.436389,4.7811453228565,4.6961111111111,4.789722,4.9207907082729,4.9787572406902,5.3458010937365,4.6029300588208,5.1797058644882,5.383478899702,5.5196324030324,5.7515738887123,5.8874461671401,5.8723225499118,6.1990994508938,6.2589770334531,6.5729701105864,6.5848470019087,6.6567253619722,7.1493220605216,6.8908745111116,3.5958241584686,3.8609657214986,4.121849767852,4.342014,4.4469005114756,4.9259216999194,4.9352386335384,5.1453989235756,5.3770039280214,5.7065946674719,5.7625447234516,5.7617834850481,6.1961067840608,xMin,xMax,xMin,xMax]
z = [4.8,5.2,5.8,5.4,5,5.3,5.4,4.6,5.8,6.3,4.8,5.4,5.3,4.6,5.4,4.4,4.1,5.5,4.5,4.2,3.9,3.7,4.2,3.2,4,3.8,2.7,2.3,3.4,2.5,3.7,5.2,2.9,5.1,3.8,4.4,4.2,3.9,3.8,3.2,2.6,2.8,2.4,3.1]
avg = np.average(z)
z.extend([avg,avg,avg,avg])
# Create map
m = Basemap(
ax = ax,
projection='merc',
llcrnrlon=xMin,
llcrnrlat=yMin,
urcrnrlon=xMax,
urcrnrlat=yMax,
resolution='h'
)
x,y = m(x,y)
m.drawmapboundary(fill_color='#d4dadc',linewidth=0.25)
# target grid to interpolate to
xis = np.arange(min(x),max(x),2000)
yis = np.arange(min(y),max(y),2000)
xi,yi = np.meshgrid(xis,yis)
# interpolate
zi = griddata((x,y),z,(xi,yi),method='cubic')
# Decide on proper values for colour bar (todo)
vrange = max(z)-min(z)
mult = 2
vmin = min(z)-(mult*vrange)
vmax = max(z)+(mult*vrange)
# Draw contours
cs = m.contour(xi, yi, zi, 5, linewidths=0.25, colors='k')
cs = m.contourf(xi, yi, zi, 5,vmax=vmax,vmin=vmin,cmap=plt.get_cmap('jet'))
for sf_name,color in zip(shapefiles, colors):
print(sf_name)
# Load data
#drawing shapes:
sf = shp.Reader(sf_name)
shapes = sf.shapes()
##print shapes[0].parts
records = sf.records()
##ax = plt.subplot(111)
for record, shape in zip(records,shapes):
lons,lats = zip(*shape.points)
data = np.array(m(lons, lats)).T
if len(shape.parts) == 1:
segs = [data,]
else:
segs = []
for i in range(1,len(shape.parts)):
index = shape.parts[i-1]
index2 = shape.parts[i]
seg = data[index:index2]
segs.append(seg)
segs.append(data[index2:])
##assuming that the longest segment is the enclosing
##line and ordering the segments by length:
lens=np.array([len(s) for s in segs])
order = lens.argsort()[::-1]
segs = [segs[i] for i in order]
##producing the outlines:
lines = LineCollection(segs,antialiaseds=(1,),zorder=4)
##note: leaving the facecolors out:
##lines.set_facecolors('#abc0d3')
lines.set_edgecolors('red')
lines.set_linewidth(0.5)
ax.add_collection(lines)
##producing a path from the line segments:
segs_lin = [v for s in segs for v in s]
codes = [
[Path.MOVETO]+
[Path.LINETO for p in s[1:]]
for s in segs]
codes_lin = [c for s in codes for c in s]
path = Path(segs_lin, codes_lin)
##patch = PathPatch(path, facecolor="#abc0d3", lw=0, zorder = 3)
patch = PathPatch(path, facecolor=color, lw=0, zorder = 3)
ax.add_patch(patch)
plt.axis('off')
fig.savefig("shapefiles.png",bbox_inches='tight',pad_inches=0)
The result looks like this:
Hope this helps.
You haven't plotted the Netherlands.
# Create map
...
# Draw contours
...
# Plot seas from shapefile
sf = shp.Reader(r'/DIR_TO_SHP/shapefiles/northsea')
...
# Plot France from shapefile
sf = shp.Reader(r'/DIR_TO_SHP/shapefiles/FRA_adm0')
...
# Plot Belgium from shapefile
sf = shp.Reader(r'/DIR_TO_SHP/shapefiles/BEL_adm0')
...
# Plot Germany from shapefile
sf = shp.Reader(r'/DIR_TO_SHP/shapefiles/DEU_adm0')
...
# Finish plot
So parts of the Netherlands are filled in with your plotted data, The coastlines are there (from drawing the seas?), but the rest of the country isn't there because you haven't drawn it.
I would steer away from this method of drawing from each shapefile individually, and try to just use basemap for your coastlines, etc. If you must do it from the shapefiles, I would encourage you to define a function that does something like:
def drawContentsOfShapeFile(mymap, path_to_shapefile):
<whatever>
#and use it:
m = Basemap(...)
myshapefiles = []
myshapefiles.append("/DIR_TO_SHP/shapefiles/FRA_adm0")
myshapefiles.append("/DIR_TO_SHP/shapefiles/norway")
for sf in myshapefiles: drawContentOfShapefile(m, sf)

Plot GDAL raster using matplotlib Basemap

I would like to plot a raster tiff (download-723Kb) using matplotlib Basemap. My raster's projection coordinates is in meter:
In [2]:
path = r'albers_5km.tif'
raster = gdal.Open(path, gdal.GA_ReadOnly)
array = raster.GetRasterBand(20).ReadAsArray()
print ('Raster Projection:\n', raster.GetProjection())
print ('Raster GeoTransform:\n', raster.GetGeoTransform())
Out [2]:
Raster Projection:
PROJCS["unnamed",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["EPSG","4326"]],PROJECTION["Albers_Conic_Equal_Area"],PARAMETER["standard_parallel_1",15],PARAMETER["standard_parallel_2",65],PARAMETER["latitude_of_center",30],PARAMETER["longitude_of_center",95],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]]]
Raster GeoTransform:
(190425.8243, 5000.0, 0.0, 1500257.0112, 0.0, -5000.0)
If I try to plot this using a Robin projection using contourf with latlon=False than x and y are assumed to be map projection coordinates (see docs, I think that's what I have).
But if I look to the plot I notice it's placed bottom left very small:
Using this code:
In [3]:
xy = raster.GetGeoTransform()
x = raster.RasterXSize
y = raster.RasterYSize
lon_start = xy[0]
lon_stop = x*xy[1]+xy[0]
lon_step = xy[1]
lat_start = xy[3]
lat_stop = y*xy[5]+xy[3]
lat_step = xy[5]
fig = plt.figure(figsize=(16,10))
map = Basemap(projection='robin',resolution='c',lat_0=0,lon_0=0)
lons = np.arange(lon_start, lon_stop, lon_step)
lats = np.arange(lat_start, lat_stop, lat_step)
xx, yy = np.meshgrid(lons,lats)
levels = [array.min(),-0.128305,array.max()]
map.contourf(xx, yy,array, levels, cmap=cm.RdBu_r, latlon=False)
map.colorbar(cntr,location='right',pad='10%')
map.drawcoastlines(linewidth=.5)
map.drawcountries(color='red')
Eventually I don't want to have a world view but a detailed view. But this gives me a zoom level where the coastlines and countries are drawn, but data is again placed in bottom left corner, but not as small as previous time:
Using the following code:
In [4]:
extent = [ xy[0],xy[0]+x*xy[1], xy[3],xy[3]+y*xy[5]]
width_x = (extent[1]-extent[0])*10
height_y = (extent[2]-extent[3])*10
fig = plt.figure(figsize=(16,10))
map = Basemap(projection='stere', resolution='c', width = width_x , height = height_y, lat_0=40.2,lon_0=99.6,)
xx, yy = np.meshgrid(lons,lats)
levels = [array.min(),-0.128305,array.max()]
map.contourf(xx, yy, array, levels, cmap=cm.RdBu_r, latlon=False)
map.drawcoastlines(linewidth=.5)
map.drawcountries(color='red')
You can use the following code to convert the coordinates, it automatically takes the projection from your raster as the source and the projection from your Basemap object as the target coordinate system.
Imports
from mpl_toolkits.basemap import Basemap
import osr, gdal
import matplotlib.pyplot as plt
import numpy as np
Coordinate conversion
def convertXY(xy_source, inproj, outproj):
# function to convert coordinates
shape = xy_source[0,:,:].shape
size = xy_source[0,:,:].size
# the ct object takes and returns pairs of x,y, not 2d grids
# so the the grid needs to be reshaped (flattened) and back.
ct = osr.CoordinateTransformation(inproj, outproj)
xy_target = np.array(ct.TransformPoints(xy_source.reshape(2, size).T))
xx = xy_target[:,0].reshape(shape)
yy = xy_target[:,1].reshape(shape)
return xx, yy
Reading and processing the data
# Read the data and metadata
ds = gdal.Open(r'albers_5km.tif')
data = ds.ReadAsArray()
gt = ds.GetGeoTransform()
proj = ds.GetProjection()
xres = gt[1]
yres = gt[5]
# get the edge coordinates and add half the resolution
# to go to center coordinates
xmin = gt[0] + xres * 0.5
xmax = gt[0] + (xres * ds.RasterXSize) - xres * 0.5
ymin = gt[3] + (yres * ds.RasterYSize) + yres * 0.5
ymax = gt[3] - yres * 0.5
ds = None
# create a grid of xy coordinates in the original projection
xy_source = np.mgrid[ymax+yres:ymin:yres, xmin:xmax+xres:xres]
Plotting
# Create the figure and basemap object
fig = plt.figure(figsize=(12, 6))
m = Basemap(projection='robin', lon_0=0, resolution='c')
# Create the projection objects for the convertion
# original (Albers)
inproj = osr.SpatialReference()
inproj.ImportFromWkt(proj)
# Get the target projection from the basemap object
outproj = osr.SpatialReference()
outproj.ImportFromProj4(m.proj4string)
# Convert from source projection to basemap projection
xx, yy = convertXY(xy_source, inproj, outproj)
# plot the data (first layer)
im1 = m.pcolormesh(xx, yy, data[0,:,:], cmap=plt.cm.jet, shading='auto')
# annotate
m.drawcountries()
m.drawcoastlines(linewidth=.5)
plt.savefig('world.png',dpi=75)
If you need the pixels location to be 100% correct you might want to check the creation of the coordinate arrays really careful yourself (because i didn't at all). This example should hopefully set you on the right track.

Categories

Resources