Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clarify how to make code that uses cartopy run in an offline environment #2329

Open
Phillip-M-Feldman opened this issue Feb 20, 2024 · 8 comments

Comments

@Phillip-M-Feldman
Copy link

The attached code works on computers with an Internet connection, but fails on a computer with no Internet connection when attempting to download files. It is unclear what in the code is triggering the downloads, and it is also unclear how to force the code to load files from a local repository. This seems like a poor design.

cartopy_wheat.zip

@JamesParrott
Copy link

You can just paste source code in triple back ticks like this:

#!/usr/bin/env python3
# code/plots/cartopy_wheat.py
# {{{
# This code accompanies the book _Python for MATLAB Development:
# Extend MATLAB with 300,000+ Modules from the Python Package Index_
# ISBN 978-1-4842-7222-0 | ISBN 978-1-4842-7223-7 (eBook)
# DOI 10.1007/978-1-4842-7223-7
# https://github.com/Apress/python-for-matlab-development
#
# Copyright © 2022 Albert Danial
#
# MIT License:
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
# }}}
import numpy as np
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.io.shapereader import Reader
from shapely.geometry.polygon import Polygon
import matplotlib.pyplot as plt
from matplotlib.colors import from_levels_and_colors
from mpl_toolkits.axes_grid1 import make_axes_locatable

from pdb import set_trace

wheat_tons = {
    'Argentina' : 13_930_078 ,
    'Brazil'    :  6_261_895 ,
    'Chile'     :  1_358_128 ,
    'Uruguay'   :  1_076_000 ,
    'Paraguay'  :    840_000 ,
    'Bolivia'   :    263_076 ,
    'Peru'      :    214_533 ,
    'Colombia'  :      9_718 ,
    'Ecuador'   :      6_584 ,
    'Venezuela' :       161 , }

Countries = Reader('nat_earth/ne_110m_admin_0_countries.shp')
shp = {} # country shape indexed by name

# for i, (geom, meta) in enumerate(zip(Countries.geometries(), Countries.records())):
#     print(f"At line 55.  i={i}")
#     name= meta.attributes['NAME_EN']
#     shp[name] = geom

Cg= list(Countries.geometries())
Cr= list(Countries.records   ())

for i in range(len(Cg)):
   meta= Cr[i]
   name= meta.attributes['NAME_EN']
   shp[name] = Cg[i]

PC = ccrs.PlateCarree()
fig, ax = plt.subplots(1, 1, subplot_kw=dict(projection=PC))
ax.set_extent([-85, -33,    # lon
               -55,  14])   # lat

ton_intervals = np.array([0, 10_000, 100_000, 1_000_000, 10_000_000, 15_000_000,])
fraction_interval = np.linspace(0.12, 1, num=len(ton_intervals))
colors = plt.cm.BuPu(fraction_interval[:-1])

for ctry in wheat_tons:
    color = colors[np.searchsorted(ton_intervals, wheat_tons[ctry]) - 1]
    shape = [shp[ctry]] if isinstance(shp[ctry], Polygon) else shp[ctry]
    ax.add_geometries(shape, crs=PC, facecolor=color)

ax.add_feature(cfeature.BORDERS)
ax.add_feature(cfeature.COASTLINE)
ax.set_title('Wheat Production, 2014 [10$^6$ tons]')

# colorbar
divider = make_axes_locatable(ax)
ax_cb = divider.new_horizontal(size="5%", pad=0.1, axes_class=plt.Axes)
fig.add_axes(ax_cb)
cmap, norm = from_levels_and_colors(ton_intervals/1.0e+6, colors)
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
plt.colorbar(sm, cax=ax_cb)

plt.show()
#plt.savefig('SA_wheat_2014.png', dpi=200,
# bbox_inches='tight', pad_inches=0, transparent=True)

@JamesParrott
Copy link

JamesParrott commented Feb 20, 2024

I've tried to reproduce this in both a Python 3.11 and a Python 3.9 venv. mpl_toolkits is not on pip. Hopefully it will install after the other deps with:

pip install --upgrade matplotlib

@JamesParrott
Copy link

JamesParrott commented Feb 20, 2024

The above did install mpl_toolkits. This allowed me to rule out Cartopy auto-magically downloading the Shapefile from OWS or some other online GIS service.

Please can you attach the shapefile you used Phillip?

line 54, in <module>
    Countries = Reader('nat_earth/ne_110m_admin_0_countries.shp')

shapefile.ShapefileException: Unable to open nat_earth/ne_110m_admin_0_countries.dbf or nat_earth/ne_110m_admin_0_countries.shp.

@JamesParrott
Copy link

JamesParrott commented Feb 20, 2024

Never mind. I can't reproduce the issue Phillip. The code in the zip file runs for me. Are you using the Fiona back end instead of the PyShp one? Something required certifi, but nothing's required requests or urllib3. So if none of the other libraries below include a http client, the code that calls out uses http.client or urllib directly. Instead of far easier alternatives.

(venv) C:\..\Downloads\cartopy>pip freeze
Cartopy==0.22.0
certifi==2024.2.2
contourpy==1.2.0
cycler==0.12.1
fonttools==4.49.0
importlib-resources==6.1.1
kiwisolver==1.4.5
matplotlib==3.8.3
numpy==1.26.4
packaging==23.2
pillow==10.2.0
pyparsing==3.1.1
pyproj==3.6.1
pyshp==2.3.1
python-dateutil==2.8.2
shapely==2.0.3
six==1.16.0
zipp==3.17.0

image

I got the shapefile from here: https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/110m/cultural/ne_110m_admin_0_countries.zip

To make a test env:

py -3.9 -m venv venv
.\venv\Scripts\activate
pip install cartopy numpy matplotlib shapely
pip install --upgrade matplotlib

@Phillip-M-Feldman
Copy link
Author

Hello James. Your output looks correct. I get the same thing on an Internet-connected computer. Have you tried on a computer without an Internet connection? If that works for you, then cartopy must be caching the files somewhere. With no cached files and no Internet connection, the code should fail.

@JamesParrott
Copy link

JamesParrott commented Feb 20, 2024

Yes - this was with the connection turned off. If you look closely, the 404 to this very page is visible behind the matplotlib window.

If this issue really is important, please provide a minimal reproducible example, and full details of your Python environment, including whether you're using Fiona or PyShp (e.g. from pip freeze), and a link to the exact shapefile you're trying to open with this code.

@greglucas
Copy link
Contributor

cfeature.STATES and cfeature.BORDERS are not included with Cartopy, so it is likely those two lines.
You can download data and supply it via the cartopy.config dictionary:
https://scitools.org.uk/cartopy/docs/latest/reference/config.html

There is also a script provided to download offline data as well.
https://github.com/SciTools/cartopy/blob/main/lib/cartopy/feature/download/__main__.py

This could probably be an additional section in the docs to explain how to do it. PRs to help with the docs are very welcome.

@Phillip-M-Feldman
Copy link
Author

Phillip-M-Feldman commented Feb 21, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants