Python Basemap Tutorial

Below is a simple introduction the the Python module Basemap. I only scratch the surface of Basemap's potential and the examples below serve to introduce students to ploting in Python (and reminders for myself!). While going through the introduction, please refer to the Basemap documenation. In the future I plan to add examples of how to collect data from EOL and GBIF using their APIs and generate plots in Basemap.

First, import the necessary modules

In [2]:
%matplotlib inline
import mpl_toolkits
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
from matplotlib.patches import PathPatch
import numpy as np

Now, we will plot a simple Robinson projection of the world

In [9]:
fig = plt.figure(figsize=(12,9))
ax = fig.add_subplot(111)
map = Basemap(projection='robin',lon_0=0,resolution='i',area_thresh=10000)
map.drawmapboundary()
map.drawcoastlines(linewidth=0.5, color='black')
map.drawstates(linewidth=1, color='black')
plt.show()

The first two lines of code are to set up the figure using Matplotlib. More detail on this can be found here. Next, we make a variable called map, which will be our map projection and the boundaries. The list of map projections and examples are located here. There are tons of parameters that can be used when making a variable with the Basemap class that can be found here. I will explore a few of these below with a specific example of South American cicadas. For now, lets explore some simple commands that add lines and colors to our map.

Lets add some color and different types of lines to our Robinson projection map

In [20]:
fig = plt.figure(figsize=(12,9))
ax = fig.add_subplot(111)
map = Basemap(projection='robin',lon_0=0,resolution='i',area_thresh=10000)
map.drawmapboundary(fill_color='#000000')
map.drawcoastlines(linewidth=0.75, color='#000000')
map.drawstates(linewidth=0.25, linestyle='-', color='#FFFFFF')
map.fillcontinents(color='#e41a1c',lake_color='#377eb8')
plt.show()

The continent colors, ocean colors, lake colors, line styles (e.g., solid or dashed), and line thickness can all be modified after a variable is created using the Basemap class (i.e., map = Basemap()). In the example above, I first call map.drawmapboundary. This colors the ocean and you can use any hex color you want or change the line style as I do below. Next I call map.drawcoastlines(linewidth=0.75, color='#000000'), which draws the coastlines for the map. Again, it can be any combination of color, line size, and line style just by changing the parameter values. Next, I outline in the state boundaries with dashed white lines with a line thickness of 0.25 using map.drawstates(linewidth=0.25, linestyle='-', color='#FFFFFF'). Note that only the US, South American and Australian states are outlined. If you want to color in specific states, you need to include a shape file. I will go over this in an example below. The last call to the map variable, map.fillcontinents(color='#e41a1c',lake_color='#377eb8'), colors the continents red and the large lakes within the continents blue.

Coloring individual countries, states, and territories

In [23]:
fig = plt.figure(figsize=(12,9))
ax = fig.add_subplot(111)
map = Basemap(projection='robin',lon_0=0,resolution='i',area_thresh=10000)
map.drawmapboundary(fill_color='#000000')
map.drawcoastlines(linewidth=0.75, color='#000000')
map.drawstates(linewidth=0.25, linestyle='-', color='#FFFFFF')
map.fillcontinents(color='#e41a1c',lake_color='#377eb8')

#Read shape file to color individual countries
shape_file = map.readshapefile('/Users/cowen/Documents/RV217/ne_10m_admin_0_countries_lakes/ne_10m_admin_0_countries_lakes', name='states', drawbounds=False)#drawbounds=True, color='black')
chile_patches = []
argentina_patches = []
for info, shape in zip(map.states_info, map.states):
    if info['NAME'] == 'Chile':
        chile_patches.append(Polygon(np.array(shape), True))
    if info['NAME'] == 'Argentina':
        argentina_patches.append(Polygon(np.array(shape), True))
ax.add_collection(PatchCollection(chile_patches, facecolor= '#4daf4a', edgecolor='#4daf4a', linewidths=0.5, zorder=2))
ax.add_collection(PatchCollection(argentina_patches, facecolor= '#ffff33', edgecolor='#ffff33', linewidths=0.5, zorder=2))
plt.show()

In order to color individual countries or territories in Basemap, you need to read in a shapfile. A shape file contains all of the boundary coordinates, which are used to color the polygons. There are lots of shapefiles on the internet and I usually use those located at Natural Earth. Once the shape file is read into Basemap, you have to search for the country name and add all of the coordinates to an empty list. In the example above, I created an empty lists for Chile and Argentina and add all coordinates in the shapefile to these empty lists. Once those coordinates are added to their respective lists, I plot them using add_collections. It is here I can change the color, edge color, line width, etc.

Basemap example: Making distribution maps for two cicada species

Lets start with the map I generated above, but first let me remove the colors and some of the boundaries.

In [28]:
fig = plt.figure(figsize=(12,9))
ax = fig.add_subplot(111)
map = Basemap(projection='robin',lon_0=0,resolution='i',area_thresh=10000)
map.drawmapboundary()
map.drawcoastlines(linewidth=0.5, color='#000000')
plt.show()

Plotting species occurences in Basemap

In [4]:
#Species coordinates
sp1_lon = [-70.70, -71.79, -71.07]#Species 1 Longitudes
sp1_lat = [-32.46, -35.62, -31.79]#Species 1 Latitudes
sp2_lon = [-70.71475, -70.49391667, -70.51766667, -71.79315, -70.43655, -70.54886667, -70.63473333]#Species 2 Longitudes
sp2_lat = [-32.58825, -33.40066667, -33.49263889, -35.62571667, -34.95656667, -34.86373333, -34.19561667]#Species 2 Latitudes

fig = plt.figure(figsize=(12,9))
ax = fig.add_subplot(111)
map = Basemap(projection='robin',lon_0=0,resolution='i',area_thresh=10000)
map.drawmapboundary()
map.drawcoastlines(linewidth=0.5, color='#000000')

#Add the occurrences to the map
x1,y1 = map(sp1_lon, sp1_lat)
map.plot(x1, y1, 'bo', markersize=12)
x2,y2 = map(sp2_lon, sp2_lat)
map.plot(x2, y2, 'ro', markersize=12)
plt.show()

In order to plot species occurences, the latitudes and longitudes need to be in list. Once they are in lists, the are made into tuples using the map command and then the points are plotted using the plot command. The parameters of the plot command can change the color, size, type of marker (e.g., circle, square, triangle, etc.), and marker edge color.

These points are in South American, but they are all on top of one another and we can not see exactly where they are; therefore, lets zoom in a bit by modifying the Basmap class command

In [41]:
#Species coordinates
sp1_lon = [-70.70, -71.79, -71.07]
sp1_lat = [-32.46, -35.62, -31.79]
sp2_lon = [-70.71475, -70.49391667, -70.51766667, -71.79315, -70.43655, -70.54886667, -70.63473333]
sp2_lat = [-32.58825, -33.40066667, -33.49263889, -35.62571667, -34.95656667, -34.86373333, -34.19561667]

fig = plt.figure(figsize=(12,9))
ax = fig.add_subplot(111)
map = Basemap(projection='merc',llcrnrlat=-56.0,urcrnrlat=14.50,llcrnrlon=-88.0,urcrnrlon=-32.0,lat_ts=-16.75,resolution='i',area_thresh=10000)
map.drawmapboundary()
map.drawcoastlines(linewidth=0.5, color='#000000')

#Add the occurrences to the map
x1,y1 = map(sp1_lon, sp1_lat)
map.plot(x1, y1, 'bo', markersize=12)
x2,y2 = map(sp2_lon, sp2_lat)
map.plot(x2, y2, 'ro', markersize=12)
plt.show()

In the above code, I changed the projection (preference, but not necessary) and altered the Basemap variable so that it focuses only on South America. It is much better now, but the points are still quite large and we do not have a good idea of where these are located. Lets see if we can improve this.

In [8]:
#Species coordinates
sp1_lon = [-70.70, -71.79, -71.07]
sp1_lat = [-32.46, -35.62, -31.79]
sp2_lon = [-70.71475, -70.49391667, -70.51766667, -71.79315, -70.43655, -70.54886667, -70.63473333]
sp2_lat = [-32.58825, -33.40066667, -33.49263889, -35.62571667, -34.95656667, -34.86373333, -34.19561667]

fig = plt.figure(figsize=(12,9))
ax = fig.add_subplot(111)
map = Basemap(projection='merc',llcrnrlat=-56.0,urcrnrlat=14.50,llcrnrlon=-88.0,urcrnrlon=-32.0,lat_ts=-16.75,resolution='i',area_thresh=10000)
map.drawmapboundary()
map.drawcoastlines(linewidth=0.5, color='#000000')
map.drawcountries()

#Add the occurrences to the map
x1,y1 = map(sp1_lon, sp1_lat)
map.plot(x1, y1, 'bo', markersize=5)
x2,y2 = map(sp2_lon, sp2_lat)
map.plot(x2, y2, 'ro', markersize=5)
plt.show()

It is better now that we added the country boundaries, but still not that great.

In [6]:
import mpl_toolkits
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
from matplotlib.patches import PathPatch
import numpy as np
%matplotlib inline

#Species coordinates
sp1_lon = [-70.70, -71.79, -71.07]
sp1_lat = [-32.46, -35.62, -31.79]
sp2_lon = [-70.71475, -70.49391667, -70.51766667, -71.79315, -70.43655, -70.54886667, -70.63473333]
sp2_lat = [-32.58825, -33.40066667, -33.49263889, -35.62571667, -34.95656667, -34.86373333, -34.19561667]

fig = plt.figure(figsize=(12,12))
ax = fig.add_subplot(111)
map = Basemap(projection='merc',llcrnrlat=-40.5,urcrnrlat=-25.5,llcrnrlon=-78.0,urcrnrlon=-65.0,lat_ts=-33.5,resolution=None,area_thresh=0.1)
map.bluemarble(scale=4.0)

#Add the occurrences to the map
x1,y1 = map(sp1_lon, sp1_lat)
ax.plot(x1, y1, 'bo', markersize=7)
x2,y2 = map(sp2_lon, sp2_lat)
ax.plot(x2, y2, 'ro', markersize=7)
plt.show()

I changed the plot background by adding the NASA Bluemarble satellite photo as a background. You can read more about the NASA Bluemarble background here.

Last but not least, I need to demonstrate how you save the figure. To save the map, add the following to the end of the code: fig.savefig('SouthAmerica_map.png', dpi=300). Here I save the figure as a png file, but also notice that I can specify the dpi. Additional parameters can be specified here by looking at the Matplotlib documentation.

These are just a few examples of how to make plots using Basemap, but there are other Python modules to make plots. Plotly and Bokeh are used primarily to generate html plots, but you can convert them to vector figures.