Download data from Natural Earth and OpenStreetMap for Cartopy

I'm trying to use cartopy to plot several maps and I want to use them offline. Cartopy has a data directory,

import cartopy.config
{'data_dir': '/home/user/.local/share/cartopy',
'downloaders': {('shapefiles',
'gshhs'): < at 0x7f3ee33ee7d0>,
 'natural_earth'): < at 0x7f3ee33ee710>},
'pre_existing_data_dir': '',
'repo_data_dir': '/home/user/bin/virtualenvs/mobi/local/lib/python2.7/site-packages/cartopy/data'}

So I believe that i can download the maps from Natural Earth site. How can I structure this data on this directory so cartopy would not use the internet to plot? And how can I do the same for OpenStreetMap data?

Asked By: Ivan

Answer #1:

(Partial answer only)

At Natural Earth web site,, you can find all the downloadable files. For example, this link provides low resolution data:

One of the data files on that page has this link address:

This piece of code will download that file (if it is not readily available on the computer):-

import cartopy
fname = \
  resolution='110m', \
  category='physical', \

fname is full pathname of the downloaded file.

You dont need to arrange the download location for cartopy. It already has default location that you can find by:

cartopy.config['data_dir']   # usually 'C:\\Users\\username\\.local\\share\\cartopy'

You can check out the files you downloaded and see how they are structured in that location.

Next time when you use cartopy function (with default config) it will use the local files if they are available.

Answered By: swatchai

Answer #2:

I had Faced similar issue wherein, with cartopy, the plt.gca().coastlines() was triggering the download of a zip file from external server, but the download was failing as internet connectivity was absent.

 /home/apps/CARTOPY/0.16.0/lib64/python2.7/site-packages/Cartopy-0.16.0-py2.7-linux-x86_64.egg/cartopy/io/ DownloadWarning: Downloading:
  warnings.warn('Downloading: {}'.format(url), DownloadWarning)

I manually downloaded the zip file , and extracted under - ~/.local/share/cartopy/shapefiles/natural_earth/physical.

~/.local/share/cartopy/shapefiles/natural_earth/physical> ls
ne_110m_coastline.README.html  ne_110m_coastline.cpg  ne_110m_coastline.prj  ne_110m_coastline.shx
ne_110m_coastline.VERSION.txt  ne_110m_coastline.dbf  ne_110m_coastline.shp

then after renaming/removing "ne_" prefix from some files, i was able to solve this issue.

~/PLOT_TEST> ls ~/.local/share/cartopy/shapefiles/natural_earth/physical/
110m_coastline.cpg  110m_coastline.dbf  110m_coastline.prj  110m_coastline.shp  110m_coastline.shx  ne_110m_coastline.README.html  ne_110m_coastline.VERSION.txt
Answered By: Puneet S. Chauhan

Answer #3:

I have prepared a code in where you can download the the shapefiles from natural earth and then convert them into a dataframe. Pay attention, the country coordinates in natural earth are in polygon and multi-polygon format. In the case of dealing with Rivers which are linestring you need to modify the code.

You might need to manipulate the "name" with your desired filename like "coastlines". Find more information in the following link:

import as shpreader
ne_earth_countries = shpreader.natural_earth(resolution = '10m',
                                       category = 'cultural',
countries = shpreader.Reader(ne_earth_countries).records()

def extract_geom_meta(country):
    coords = np.empty(shape=[0,2])
    for geom in country.geometry:
        coords = np.append(coords, geom.exterior.coords, axis=0)
    country_name = country.attributes["ADMIN"]
    return [country_name, coords]

WorldDF = pd.DataFrame([extract_geom_meta(country) for country in countries],

CountryDF = pd.concat([pd.DataFrame(WorldDF['coordinates'][country_idx])
                            for country_idx in range(len(WorldDF))]).reset_index()

CountryDF['Label'] = CountryDF.apply(lambda row: 1 if row['index'] == 0
                                                        else 0,axis=1).cumsum()-1

CountryDF['Country'] = CountryDF['Label'].apply(lambda row: WorldDF.countries[row])

CountryDF.rename(columns={0:'Longitude', 1:'Latitude'},inplace=True)
Answered By: Navid
The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .

# More Articles