Geography

How to get bounding box of a country using Natural Earth data and Cartopy

In this example, we’ll determine the bounding box of Kenya using the public domain Natural Earth dataset and the Cartopy library.

Rendering just the bounding box of Kenya (with the actual country being highlighted in green) looks like this:

How to get the bounding box

First we use Cartopy’s cartopy.io.shapereader.natural_earth() function that will automatically download Natural Earth data (if it has already been downloaded, the cached data will be used):

shpfilename = shpreader.natural_earth(resolution='10m',
                                      category='cultural',
                                      name='admin_0_countries')
reader = shpreader.Reader(shpfilename)

Now we can filter for Kenya just like we did in our previous post on How to highlight a specific country using Cartopy:

kenya = [country for country in reader.records() if country.attributes["NAME_LONG"] == "Kenya"][0]

and get the bounding box using kenya.bounds:

lon_min, lat_min, lon_max, lat_max = kenya.bounds

Complete example code

This code will render the image shown above:

import cartopy.crs as ccrs
import cartopy.feature as cf
from cartopy.feature import ShapelyFeature
from matplotlib import pyplot as plt

proj = ccrs.PlateCarree()
ax = plt.axes(projection=proj)
# Show only Africa
#ax.set_extent([-23, 55, -35, 40])
ax.stock_img()

ax.add_feature(cf.COASTLINE, lw=2)
# Make figure larger
plt.gcf().set_size_inches(20, 10)

import cartopy.io.shapereader as shpreader
# Read shape file
shpfilename = shpreader.natural_earth(resolution='10m',
                                      category='cultural',
                                      name='admin_0_countries')
reader = shpreader.Reader(shpfilename)
# Filter for a specific country
kenya = [country for country in reader.records() if country.attributes["NAME_LONG"] == "Kenya"][0]
# Determine bounding box
lon_min, lat_min, lon_max, lat_max = kenya.bounds
ax.set_extent([lon_min, lon_max, lat_min, lat_max])

# Display Kenya's shape
shape_feature = ShapelyFeature([kenya.geometry], ccrs.PlateCarree(), facecolor="lime", edgecolor='black', lw=1)
ax.add_feature(shape_feature)

# Save figure as SVG
plt.savefig("Kenya-Bounding-Box-Tight.svg")

 

Posted by Uli Köhler in Cartopy, Geography, Python

How to plot Shapefile data in Cartopy

In order to display shapefile data in Cartopy, we can first use the cartopy.io.shapereader package to read the shape data and then convert the geometry we want to display to a cartopy.feature.ShapelyFeature.

In the following example, we’ll read the Natural Earth ne_110m_admin_0_countries.shp and
Note that there’s an easier way to plot Natural Earth data using shpreader.natural_earth – see How to highlight a specific country using Cartopy and we’ll use the Natural Earth dataset just as an example!

import cartopy.io.shapereader as shpreader
# Read shape file
reader = shpreader.Reader("ne_110m_admin_0_countries.shp")
# Filter for a specific country
kenya = [country for country in reader.records() if country.attributes["NAME_LONG"] == "Kenya"][0]

# Display Kenya's shape
shape_feature = ShapelyFeature([kenya.geometry], ccrs.PlateCarree(), facecolor="lime", edgecolor='black', lw=1)
ax.add_feature(shape_feature)

Complete code example

import cartopy.crs as ccrs
import cartopy.feature as cf
from cartopy.feature import ShapelyFeature
from matplotlib import pyplot as plt

proj = ccrs.PlateCarree()
ax = plt.axes(projection=proj)
# Show only Africa
ax.set_extent([-23, 55, -35, 40])
ax.stock_img()

ax.add_feature(cf.COASTLINE, lw=2)
# Make figure larger
plt.gcf().set_size_inches(20, 10)

import cartopy.io.shapereader as shpreader
# Read shape file
reader = shpreader.Reader("ne_110m_admin_0_countries.shp")
# Filter for a specific country
kenya = [country for country in reader.records() if country.attributes["NAME_LONG"] == "Kenya"][0]

# Display Kenya's shape
shape_feature = ShapelyFeature([kenya.geometry], ccrs.PlateCarree(), facecolor="lime", edgecolor='black', lw=1)
ax.add_feature(shape_feature)

# Save figure as SVG
plt.savefig("Africa-Highlight-Kenya.svg")
Posted by Uli Köhler in Cartopy, Geography, Python

How to highlight a specific country using Cartopy

In our previous posts, e.g. How to draw Africa map using Cartopy we showed how to draw an overview map of an entire continent using Cartopy. This post provides an example of how to highlight a specific country in that map. In this example, we’ll highlight Kenya

The general approach is:

  1. Use cartopy.io.shapereader.natural_earth to download Natural Earth data that contains the shape of Kenya
  2. Convert it to a cartopy.feature.ShapelyFeature
  3. Display said feature

Displaying Kenya’s Natural Earth shape in cartopy

First, we create a Reader for the Natural Earth data. Cartopy will automatically download the data if it has not been cached.

import cartopy.io.shapereader as shpreader

shpfilename = shpreader.natural_earth(resolution='110m',
                                      category='cultural',
                                      name='admin_0_countries')
reader = shpreader.Reader(shpfilename)

Now we can select Kenya by name from the records:

kenya = [country for country in reader.records() if country.attributes["NAME_LONG"] == "Kenya"][0]

In order to display that geometry, we use

shape_feature = ShapelyFeature([kenya.geometry], ccrs.PlateCarree(), facecolor="lime", edgecolor='black', lw=1)
ax.add_feature(shape_feature)

Complete example code

import cartopy.crs as ccrs
import cartopy.feature as cf
from cartopy.feature import ShapelyFeature
from matplotlib import pyplot as plt

proj = ccrs.PlateCarree()
ax = plt.axes(projection=proj)
# Show only Africa
ax.set_extent([-23, 55, -35, 40])
ax.stock_img()

ax.add_feature(cf.COASTLINE, lw=2)
# Make figure larger
plt.gcf().set_size_inches(20, 10)

# Read Natural Earth data
import cartopy.io.shapereader as shpreader

shpfilename = shpreader.natural_earth(resolution='110m',
                                      category='cultural',
                                      name='admin_0_countries')
reader = shpreader.Reader(shpfilename)
kenya = [country for country in reader.records() if country.attributes["NAME_LONG"] == "Kenya"][0]

# Display Kenya's shape
shape_feature = ShapelyFeature([kenya.geometry], ccrs.PlateCarree(), facecolor="lime", edgecolor='black', lw=1)
ax.add_feature(shape_feature)

# Save figure as SVG
plt.savefig("Africa-Highlight-Kenya.svg")

 

Posted by Uli Köhler in Cartopy, Geography, Python

How to draw Europe map using Cartopy

We can easily draw an Africa Map using Cartopy by setting the extents to [-13, 45, 30, 70]:

ax.set_extent([-13, 45, 30, 70])

Complete code example

The code above produces the image shown above:

import cartopy.crs as ccrs
import cartopy.feature as cf
from matplotlib import pyplot as plt

proj = ccrs.Miller()
ax = plt.axes(projection=proj)
ax.set_extent([-13, 45, 30, 70])
ax.stock_img()

ax.add_feature(cf.COASTLINE, lw=2)
ax.add_feature(cf.BORDERS)
# Make figure larger
plt.gcf().set_size_inches(20, 10)

# Save figure as SVG
plt.savefig("Europe.svg")

 

Posted by Uli Köhler in Cartopy, Geography, Python

How to make Cartopy coastline or border lines thicker (line width)

By standard, Cartopy draws every feature with the same line width:

ax.add_feature(cf.COASTLINE)
ax.add_feature(cf.BORDERS)

We can easily increasing the line width by adding e.g. lw=2 to the ax.add_feature() call:

ax.add_feature(cf.COASTLINE, lw=2)
ax.add_feature(cf.BORDERS)

Complete code example

This example produces the image with a wider coast line line width as shown above

import cartopy.crs as ccrs
import cartopy.feature as cf
from matplotlib import pyplot as plt

proj = ccrs.PlateCarree()
ax = plt.axes(projection=proj)
ax.set_extent([-23, 55, -35, 40])
ax.stock_img()

ax.add_feature(cf.COASTLINE, lw=2)
ax.add_feature(cf.BORDERS)
# Make figure larger
plt.gcf().set_size_inches(20, 10)

# Save figure as SVG
plt.savefig("Africa-Standard.svg")

 

Posted by Uli Köhler in Cartopy, Geography, Python

How to draw Africa map using Cartopy

We can easily draw an Africa Map using Cartopy by setting the extents to [-23, 55, -35, 40]:

ax.set_extent([-23, 55, -35, 40])

Complete code example

The code above produces the image shown above:

import cartopy.crs as ccrs
import cartopy.feature as cf
from matplotlib import pyplot as plt

proj = ccrs.PlateCarree()
ax = plt.axes(projection=proj)
ax.set_extent([-23, 55, -35, 40])
ax.stock_img()

ax.add_feature(cf.COASTLINE, lw=2)
ax.add_feature(cf.BORDERS)
# Make figure larger
plt.gcf().set_size_inches(20, 10)

# Save figure as SVG
plt.savefig("Africa.svg")

 

Posted by Uli Köhler in Cartopy, Geography, Python

How to draw straight line between two coordinates using Cartopy

In our previous posts Minimal Geodetic example using Cartopy and How to increase Geodetic resolution / accuracy / smoothness in Cartopy we have shown how to create a geodetic line on a map. While a geodetic is defined to be the shortest line on the Earth’s surface, it is not a straight line on a map projection.

In order to plot a geodetic we use:

plt.plot([lon1, lon2], [lat1, lat2], transform=ccrs.Geodetic())

In order to plot a straight line, we need to use the same projection as we used to create the map instead of transform=ccrs.Geodetic().

For example, if we created the map using

plt.axes(projection=ccrs.PlateCarree())

we need to plot the line using transform=ccrs.PlateCarree()

plt.plot([lon1, lon2], [lat1, lat2], transform=ccrs.PlateCarree())

In order to avoid errors, I strongly recommend using just one instance of the projection and assigning it to a common variable, for example:

proj = ccrs.PlateCarree()
ax = plt.axes(projection=proj)
plt.plot([-75, 77.23], [43, 28.61], transform=proj)

Note that for reasons currently unknown to me at the moment, this only works for some projections at the moment. Using ccrs.PlateCarree() and ccrs.Miller() works, but using ccrs.Mollweide() does not work!

Complete example

This code reproduces the image shown above:

import cartopy.crs as ccrs
import cartopy.feature as cf
from matplotlib import pyplot as plt

proj = ccrs.PlateCarree()
ax = plt.axes(projection=proj)
ax.stock_img()
ax.add_feature(cf.BORDERS)
# Add straight line between two points
# Format: plot([lon1, lon2], [lat1, lat2])
plt.plot([-75, 77.23], [43, 28.61], linestyle='--',
         color='blue', linewidth=8,
         transform=proj)
# Make figure larger
plt.gcf().set_size_inches(20, 10)

# Save figure as SVG
plt.savefig("Cartopy-Straight-Line-PlateCarree.svg")

 

Posted by Uli Köhler in Cartopy, Geography, Python

How to increase Geodetic resolution / accuracy / smoothness in Cartopy

In our previous post we have detailed how to draw a geodetic in Cartopy. However, as you can see in the resulting map, the geodetic line is broken up into several clearly visible segments:

In order to fix that, we need to subclass the original projection of the plot. In this example, we’re subclassing ccrs.Mollweide():

import cartopy.crs as ccrs

class HighResMollweide(ccrs.Mollweide):
    @property
    def threshold(self): return 100.0

Note that the default threshold for the Mollweide projection is 100000.0 – you can check for yourself using print(ccrs.Mollweide().threshold)

Now we can use that projection in plt.axes():

ax = plt.axes(projection=HighResMollweide())

Complete example code

This code reproduced the high-resolution geodetic image shown above:

import cartopy.crs as ccrs
import cartopy.feature as cf
from matplotlib import pyplot as plt

class HighResMollweide(ccrs.Mollweide):
    @property
    def threshold(self): return 100.0

ax = plt.axes(projection=HighResMollweide())
ax.stock_img()
ax.add_feature(cf.BORDERS)
# Add geodetic between two points
# Format: plot([lon1, lon2], [lat1, lat2])
plt.plot([-75, 77.23], [43, 28.61],
         color='blue', linewidth=2,
         transform=ccrs.Geodetic()
         )
# Make figure larger
plt.gcf().set_size_inches(20, 10)

# Save figure as SVG
plt.savefig("Cartopy-Geodetic-HiRes.svg")

Thanks to @ajdawson on StackOverflow for the original hint on how to solve this!

Posted by Uli Köhler in Cartopy, Geography, Python

Minimal Geodetic example using Cartopy

This minimal example shows you how to plot a geodetic line between two points. It is closely based on the cartopy with matplotlib intro.

import cartopy.crs as ccrs
import cartopy.feature as cf
from matplotlib import pyplot as plt
ax = plt.axes(projection = ccrs.Mollweide())
ax.stock_img()
ax.add_feature(cf.BORDERS)
# Add geodetic between two points
# Format: plot([lon1, lon2], [lat1, lat2])
plt.plot([-75, 77.23], [43, 28.61],
         color='blue', linewidth=2,
         transform=ccrs.Geodetic()
         )

 

Complete example code

This code reproduces the image shown above:

import cartopy.crs as ccrs
import cartopy.feature as cf
ax = plt.axes(projection = ccrs.Mollweide())
ax.stock_img()
ax.add_feature(cf.BORDERS)
# Add geodetic between two points
# Format: plot([lon1, lon2], [lat1, lat2])
plt.plot([-75, 77.23], [43, 28.61],
         color='blue', linewidth=2,
         transform=ccrs.Geodetic()
         )
# Make figure larger
plt.gcf().set_size_inches(20, 10)

# Save figure as SVG
plt.savefig("Cartopy-Geodetic.svg")

 

Posted by Uli Köhler in Cartopy, Geography, Python

How to add colored background to Cartopy map

Want to get from this black and white map

to this colored map

in just one line of code? Simply use cartopy’s stock_img():

ax.stock_img()

Complete example code

import cartopy.crs as ccrs
import cartopy.feature as cf
from matplotlib import pyplot as plt
ax = plt.axes(projection = ccrs.Mollweide())
ax.stock_img()
ax.add_feature(cf.COASTLINE)
ax.add_feature(cf.BORDERS)
# Make figure larger
plt.gcf().set_size_inches(20, 10)

# Save figure as SVG
plt.savefig("Cartopy-Colored.svg")

Posted by Uli Köhler in Cartopy, Geography, Python

How to draw country borders in Cartopy

Use

import cartopy.crs as ccrs
import cartopy.feature as cf
from matplotlib import pyplot as plt
ax = plt.axes(projection = ccrs.Mercator())
# This will add borders
ax.add_feature(cf.BORDERS)

The following code shows you a minimal example of how to plot country borders (and coastlines) using cartopy:

import cartopy.crs as ccrs
import cartopy.feature as cf
ax = plt.axes(projection = ccrs.Mercator())
ax.add_feature(cf.COASTLINE)
ax.add_feature(cf.BORDERS)
# Make figure larger
plt.gcf().set_size_inches(20, 10)

# Save figure as SVG
plt.savefig("Cartopy-Borders.svg")

Posted by Uli Köhler in Cartopy, Geography, Python

Cartopy minimal example with Coastlines

This example shows you a minimal example of how to plot coastlines using cartopy:

import cartopy.crs as ccrs
import cartopy.feature as cf
from matplotlib import pyplot as plt
ax = plt.axes(projection = ccrs.Mercator())
ax.add_feature(cf.COASTLINE)
# Make figure larger
plt.gcf().set_size_inches(20, 10)

# Save figure as SVG
plt.savefig("Cartopy-Coastlines.svg")

Posted by Uli Köhler in Cartopy, Geography, Python

How to draw Africa-focused map using matplotlib basemap

Note that matplotlib basemap is deprecated in favour of cartopy !

Also see:

This code allows you to draw an map that is focused on Africa using matplotlib basemap:

my_map = Basemap(projection='ortho', lat_0=10, lon_0=13, resolution='l')

Complete example code

This code reproduces the image shown above:

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
my_map = Basemap(projection='ortho', lat_0=10, lon_0=13, resolution='l')
my_map.drawcoastlines(linewidth=1)
my_map.drawcountries(linewidth=0.5)

# Make plot larger
plt.gcf().set_size_inches(20, 10)
# Save to file
plt.savefig("Africa.svg")

 

Posted by Uli Köhler in Geography, Python

How to draw Europe-focused map using matplotlib basemap

Also see:

This code allows you to draw an map that is focused on Europe using matplotlib basemap:

my_map = Basemap(projection='ortho', lat_0=55, lon_0=13, resolution='l')

Complete example code

This code reproduces the image shown above:

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
my_map = Basemap(projection='ortho', lat_0=55, lon_0=13, resolution='l')
my_map.drawcoastlines(linewidth=1)
my_map.drawcountries(linewidth=0.5)

# Make plot larger
plt.gcf().set_size_inches(20, 10)
# Save to file
plt.savefig("Europe.svg")

 

Posted by Uli Köhler in Geography, Python

How to draw South-America-focused map using matplotlib basemap

Note that matplotlib basemap is deprecated in favour of cartopy !

Also see:

This code allows you to draw an map that is focused on South America using matplotlib basemap:

my_map = Basemap(projection='ortho', lat_0=-13, lon_0=-55, resolution='l')

Complete example code

This code reproduces the image shown above:

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
my_map = Basemap(projection='ortho', lat_0=-13, lon_0=-55, resolution='l')
my_map.drawcoastlines(linewidth=1)
my_map.drawcountries(linewidth=0.5)

# Make plot larger
plt.gcf().set_size_inches(20, 10)
# Save to file
plt.savefig("South America.svg")

 

Posted by Uli Köhler in Geography, Python

How to draw North-America-focused map using matplotlib basemap

Note that matplotlib basemap is deprecated in favour of cartopy !

Also see:

This code allows you to draw an map that is focused on North America using matplotlib basemap:

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
my_map = Basemap(projection='ortho', lat_0=45 ,lon_0=-100, resolution='l')
my_map.drawcoastlines(linewidth=1)
my_map.drawcountries(linewidth=0.5)

# Make plot larger
plt.gcf().set_size_inches(20, 10)
# Save to file
plt.savefig("North America.svg")

 

Posted by Uli Köhler in Geography, Python

How to fix Python ModuleNotFoundError: No module named ‘osmium’

Problem:

When you running your python script, you see an error message like

Traceback (most recent call last):
  File "run.py", line 2, in <module>
    import osmium as osm
ModuleNotFoundError: No module named 'osmium

Solution:

Install pyosmium using

pip install osmium

or

pip3 install osmium

 

 

Posted by Uli Köhler in OpenStreetMap, Python

Minimal example how to read .osm.pbf file using Python & osmium

This minimal example uses the osmium python binding to read an .osm.pbf file and count the number of nodes, ways and relations.

First, we need to download a suitable dataset. For this example, we’ll be using kenya-latest.osm.pbf which you can download from Geofabrik:

wget https://download.geofabrik.de/africa/kenya-latest.osm.pbf

Now we can run the script:

#!/usr/bin/env python3
import osmium as osm

class OSMHandler(osm.SimpleHandler):
    def __init__(self):
        osm.SimpleHandler.__init__(self)
        self.node_count = 0
        self.way_count = 0
        self.relation_count = 0

    def node(self, n):
        self.node_count += 1

    def way(self, w):
        self.way_count += 1

    def relation(self, r):
        self.relation_count += 1

osmhandler = OSMHandler()
osmhandler.apply_file("kenya-latest.osm.pbf")
print(f'Number of nodes: {osmhandler.node_count}')
print(f'Number of way: {osmhandler.way_count}')
print(f'Number of relations: {osmhandler.relation_count}')

Example output:

Number of nodes: 29442019
Number of way: 3188550
Number of relations: 2225

 

Posted by Uli Köhler in OpenStreetMap, Python