Turning ripples into waves

Plotting shapefiles with cartopy and folium

First we will use cartopy's shapereader to download (and cache) states shapefile with 50 meters resolution from the NaturalEarth.

In [2]:
from import shapereader

kw = dict(resolution='50m', category='cultural',

states_shp = shapereader.natural_earth(**kw)
shp = shapereader.Reader(states_shp)

Now let's create a list of a few states that we want to highlight.

(The unicode_literals is needed to compare the Unicode names with the decoded names from latin-1. Argh!!! One day all of python will be Python3 and this ordeal will be over.)

In [3]:
from __future__ import unicode_literals

states = ('Minas Gerais', 'Mato Grosso', 'Goiás',
          'Bahia', 'Rio Grande do Sul', 'São Paulo')

Let's plot it all with cartopy.

In [4]:
import as ccrs
import matplotlib.pyplot as plt

subplot_kw = dict(projection=ccrs.PlateCarree())

fig, ax = plt.subplots(figsize=(7, 11),
ax.set_extent([-82, -32, -45, 10])


for record, state in zip(shp.records(), shp.geometries()):
    name = record.attributes['name'].decode('latin-1')
    if name in states:
        facecolor = 'DarkOrange'
        facecolor = 'LightGray'
    ax.add_geometries([state], ccrs.PlateCarree(),
                      facecolor=facecolor, edgecolor='black')
/home/filipe/miniconda3/envs/Blog/lib/python2.7/site-packages/matplotlib/ MatplotlibDeprecationWarning: This has been deprecated in mpl 1.5, please use the
axes property.  A removal date has not been set.
  warnings.warn(_get_axes_msg, mplDeprecation, stacklevel=1)

Cartopy's maps are great, but they are not interactive. We can fix that by plotting the same data over a folium Map instance.

The trick is to save the shapefile as a GeoJSON and plot it with folium's .geo_json.

In [5]:
import os
import shapefile
from json import dumps

def shape2json(fname, outfile="states.json", country='Brazil'):
    reader = shapefile.Reader(fname)
    fields = reader.fields[1:]
    field_names = [field[0] for field in fields]

    data = []
    for sr in reader.shapeRecords():
        atr = dict(zip(field_names, sr.record))
        geom = sr.shape.__geo_interface__
        if country in sr.record[field_names.index('admin')]:
            name = sr.record[field_names.index('name')].decode('latin-1')
            if name in states:
                data.append(dict(type="Feature", geometry=geom, properties=atr))
    keys = ['abbrev', 'name', 'name_alt']
    for b in data:
        for key in keys:
            b['properties'][key] = b['properties'][key].decode('latin-1')

    with open(outfile, "w") as geojson:
        geojson.write(dumps({"type": "FeatureCollection",
                             "features": data}, indent=2) + "\n")

shape = '50m_admin_1_states_provinces.shp'
cartopy_cache = '.local/share/cartopy/shapefiles/natural_earth/cultural/'
fname = os.path.join(os.path.expanduser('~'), cartopy_cache, shape)

shape2json(fname, outfile="states.json", country='Brazil')
In [6]:
import folium
import numpy as np
from IPython.display import IFrame

def inline_map(m, width=650, height=500):
    """Takes a folium instance and embed HTML."""
    srcdoc = m.HTML.replace('"', '"')
    embed = HTML('<iframe srcdoc="{}" '
                 'style="width: {}px; height: {}px; '
                 'border: none"></iframe>'.format(srcdoc, width, height))
    return embed

bbox = [-82, -32, -45, 10]

lon_center, lat_center = np.array(bbox).reshape(2, 2).mean(axis=0)
mapa = folium.Map(width=650, height=500, zoom_start=4,
                  location=[-15, -50])

mapa.geo_json(geo_path='states.json', fill_color='none', line_color='Orange')

That was easy! Check here to learn more about GeoJSONs plotting with folium.

In [7]:

This post was written as an IPython notebook. It is available for download or as a static html.

Creative Commons License
python4oceanographers by Filipe Fernandes is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Based on a work at