Kristoffer over at rpsychologist.com wrote a really nice tutorial on getting up to speed with maps in R. It looked something like this.

png

Felt a little left out, so I decided to make a port in Python. You can follow along below or if you prefer this as an Jupyter Notebook that you can download and run on our local machine.

To start with you need the packages numpy and matplotlib basemap. The latter should only be installed through Anaconda which is your, from here on, favorite Python package installer. So let’s load them up.

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from mpl_toolkits.basemap import Basemap

Kristoffer favored the Winkel tripel projection since it’s adapted by the National Geographic Society. But since it’s unfortunaly not avalible in Basemap, we use Kavrayskiy 7 (kav7), which is very very close. And by the way, Basemap supports 34 different projections so you will probably be able to find something that suits you.

m = Basemap(resolution='c',
            projection='kav7',
            lat_0=0., # Center around
            lon_0=0.) # lat 0, lon 0

Kristoffer used graticlues (the grid) from a shapefile, Basemap handles this for us so let’s prepare what it need.

n_graticules = 18
parallels = np.arange(-80., 90, n_graticules)
meridians = np.arange(0., 360., n_graticules)
lw = 1
dashes = [5,7] # 5 dots, 7 spaces... repeat
graticules_color = 'grey'

The rest should be quite self explainatory.

fig1 = plt.figure(figsize=(16,20))
fig1.patch.set_facecolor('#e6e8ec')
ax = fig1.add_axes([0.1,0.1,0.8,0.8])

m.drawmapboundary(color='white', 
                  linewidth=0.0, 
                  fill_color='white')
m.drawparallels(parallels, 
                linewidth=lw, 
                dashes=dashes, 
                color=graticules_color)
m.drawmeridians(meridians, 
                linewidth=lw, 
                dashes=dashes, 
                color=graticules_color)
m.drawcoastlines(linewidth=0)
m.fillcontinents('black', 
                 lake_color='white')
m.drawcountries(linewidth=1, 
                linestyle='solid', 
                color='white', 
                zorder=30)

title = plt.title('World map (Kavrayskiy 7)', 
                  fontsize=20) 
title.set_y(1.03) # Move the title a bit for niceness

png

Let’s add some shapefiles. Basemap does this nicely for us with readshapefile and we use the same as Kristoffer found here. Since we named it populated_places, m.populated_places_info will hold all data. For some shapefiles this means statistics and all sorts of nice things so be sure to check m.yourname_info out.

We use m.scatter to make the bubble plot.

m.readshapefile('shapefiles/ne_110m_populated_places/ne_110m_populated_places', 
                name='populated_places', 
                drawbounds=False, 
                color='none')

populations = [r['POP2000'] for r in m.populated_places_info]
lats = [r['LATITUDE'] for r in m.populated_places_info]
lons = [r['LONGITUDE'] for r in m.populated_places_info]
x1, y1 = m(lons, lats) # Convert coords to projected place in figure
m.scatter(x1, y1, 
          s=np.array(populations)*0.05, 
          marker="o", 
          color='#32caf6',
          zorder=10, 
          alpha=0.8)

png

But most likely a shapefile will contain a shape that you want to put on your map. Let’s put out the borders of the tectonic plates on our map. The tectonic plate shapefile can be found here.

m.readshapefile('shapefiles/tectonicplates-master/PB2002_plates', 
                name='tectonic_plates', 
                drawbounds=True, 
                color='red')

png

But you’ll probably also want to have control over the settings of the shapes e.g. color the Australia plate in a different way

for info, shape in zip(m.tectonic_plates_info, 
                       m.tectonic_plates):

    if info['PlateName'] == "Australia":
        x, y = zip(*shape) 
        m.plot(x, y, marker=None, color='b')

png

And perhaps you also want to fill the patch.

from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
from matplotlib.patches import PathPatch
        
patches = []
for info, shape in zip(m.tectonic_plates_info, 
                       m.tectonic_plates):

    if info['PlateName'] == "Australia":
        patches.append(Polygon(np.array(shape), True))
        
ax.add_collection(PatchCollection(patches, 
                                  facecolor='#32caf6', 
                                  edgecolor='none', 
                                  linewidths=1., 
                                  alpha=0.5,
                                  zorder=1))

png