Atmos CEE Masthead

Rage-Quit Session: Mapping in Python

This is a deep dive with a lot of repeative plotting to demonstrate how features are added to a graphical object and the impact each one makes to the entire product. The goal with this stragegy is that it will let us slowly explore how the more advanced graphics in Python work, albiet at the cost of a much longer Python notebook.

In this "Let's Play" session, we will explore the aspects of plotting graphics beyond the simple way that we've been doing it through the semester.

One reason for this is that when one goes to the "online 'help,'" the examples that are often provided for "how to make a plot" for example tends to expect a user to jump in and intuit what is happening.

Add the more abstract concepts of looking at map projections and how they are managed in any kind of software and speaking for me, personally, this is an engraved invitation to rage quit, which I did -- more than once -- when transitioning to Python. Therefore, in this session we are going to slow-roll the process of working with plots that have maps on them.

This tutorial is NOT about plotting meteorological or other geosiatial gridded data. Here we are ONLY going to do some rudementary plotting of maps with nothing more than a few points and lines on top of them.

Prerequisite Nerd Skills

For this exercize to get started it's a good idea to have these skills under your belt.

Libraries

The classics...

To begin we'll load our libraries with two previous old standards:

... And Map Projections

Some of you may need to work with geospatial data. We actually can use the above example to show us the way.

We had declared a new "projection" for our polar plot called, "polar."

Now, imagine if you could now plot data in a different cooridate framework, such as latitude and longitude.

This will require us to load a new package, Cartopy. Which contains information as well as the means to plot basic map features like continents and countries.

Depending on what your application is you may need to install more than one package.

I recommend that it be done this way if you are using anaconda or miniconda.

!conda install shapely cartopy

from inside Jupyter. But you will also need to restart your Jupyter Kernel for it to take hold

You can then load the suplemental libraries. You should only need the cartopy library to get this going, but you will need to install it much like when you load libaries from scipy.

Implicitly loaded with Cartopy will also be the the Shapely Library.

Shapely's job is to manipilate and transform objects in a 2-d cartesian plan. Cartesian planes in this context are just "maps." For example, a map will have polygons, lines, etc corresponding to locatoins, borders, coastlines, etc, and later on as we explore, 2-D and 3-D grids. Typically these will be in a given projection such as Polar Stereographic, or Lambert Conformal Conic, or in terms of latitude and longitude. Shapely bends and warps these fields to fit a projection or to convert projections into latitude and longitude.

Note that Shapely is about to go through major new release. At the time of me writing this, the current version is 1.8. The next expected version, 2.0, will have a few major changes from the 1.* editions of Shapely. Therefore, when running things that will leverages these changes, even if you are not directly using the features, your notebook session will be dripping with so much pink you'll think Strawberry Shortcake killed the Klingon Chancelor with her teeth and claws alone. I'm trying to limit my use here of Cartopy and Shapely to the bare minium of what we need to plot in this exercise.

Cutting a single panel map.

To start let's drop an empty Axis with a projection onto our workspace.

For this example, I'm starting with a global projection called the "Interrupted Goode Homolosine" projection, a projection you may often see in atlases that resembles a peeled origins.

Here apart from showing the outlines of the classic Goodd Homolosine "orange peel." That's ok. Because we really need to decide what the map is going to be of.

Will it just have continents? Will we want to zoom in? What features do we want to highlight borders, terrain height, something else?

But for now we are going to do a very simple line map. To do so, we will dig into the "features" library in Cartopy and just grab the coastlines.

To add a geographical feature use this example which leverages a command designed to use with Matplotlib, add.feature(). The link with this command includes information on some basic map global scale maps as well as US state boundaries.

(I am also going to change projections to the Robinson Projection which is a simple global coverage without having to "rip" the Earth's surface like we do with the Goode projection. (I'll be showing other projections as we play.)

A map is nice... but at some point we want to put our own data on it.

Let's play with known places on the globe

To do this here is where it gets a little tricky. For example, plotting data in latitude and longitude would be intuitive. But in reality you are plotting in an x-y map space in which rather than latitude and longitude, you are plotting in "eastings" and "northings."

The good news is that your base background can use one projection scheme while you can plot data in another map projetion. Notice the syntax in the examples below. We are adding a "new" argument to the plot command: "transform," which you may remember can also be used in plotting in simple polar coordinates with r, and $\theta$.

Trolling the Flat Earthers (making lines in a specific vector)

If you have heard about the "flat earther" craze, they project their world in what is often a polar projection.

Fun fact... the Soviets had this as their go-to projection as well which resulted in a very different worldview (literally) than United States who often uses a Mercator Projection, Robinson- or Goode-style projection when plotting things globaly. This comes as a surprise to a lot of people but in Meteorology, the Mercator projection is often a prefered projection in Numerical Weather Prediction (NWP) when working in the Tropics since a Mercator Projection is a "conformal" projection, meaning that it preserves angles -- i.e., vectors.

This leads to some genuine bemusent when they ahve to explain ceratin aircraft routes.

Let's repeat the previous graph in the Polar Stereographic Projection, which, BTW, is popilar as you move poleward in NWP.

Also from above, you can see that if you chose to point a set area of points, the total map extent will "clip" to the fit the data available. We can fix that with the axes.set_global() modifier (which acutally sits under Cartopy's library.

We are also going to add grid lines.

Looking at the above problem, you can start to see the Sydney-to-Santiago flight from the Flat Earther's perspective. Traveling from Sydney to Santiago SHOULD be imposible given the lenght of travel needed in their worldview. Indeed, they believe that Qantus Flights 27 & 28, LATAM Flights 802 & 803 are myths and similar long hauls in the Southern Hemisphere are hoaxes. Rather, they argue that the real flights operate, for example as SYD->LAX->SCL -- which in reality reflects the economics of air travel given that the market for direct flights between Chile & Australia aren't as profitable (and thus, as common) as ~car~plane-pooling between more frequent trips like the daily trips passing through more global hubs like LAX.

So let's map out a few routes.

When we do this, you will want to have our line-of-sight paths using a different projection: Specifcally the Geodedic Projection. This will honor a spherical earth regardless of the projection you are using and provide a more credible "as the crow flies" distance.

Checkmate Flat Earthers!

While that last flight looks like a helluva miles run, remember that they're in the southern hemisphere. So let's redo those flights keeping our domestic flights domestic and that one big one using a more approrate projection such as the South Polar Stereographic Projection!

Also since the path will still be long and the projected straight line (on a sphere) looks like a curve in this projection, a careful look at the QA 27 flight path looks like a collection of line segments rather than an smooth curve. This is because the rendering of lines in projected space are based on a default map projection's distance threshold length -- the longest line it will draw. We will want small line segment lengths as we draw our circular paths.

To do this, the second plot below illustrates how that can be done: You must "clone" the projection that your MAP is on into a new variable. Then you can manipulate various parameters of the projection -- for us that is just the "threshold" attribute. The example is here and the second plot shows it in action.

hirezSouthPS = ccrs.SouthPolarStereo()
hirezSouthPS._threshold = hirezSouthPS._threshold/100.

And let's try another projection. The classic "Great Circle" routes are geodedic "as the crow flies" paths placed in a classic Mercator [conformal] projection.

This plot also uses cartopy.mpl.geoaxes.GeoAxes.set_extent() to fine tune the extents so give us a broader global perspective of our flights in the context of the big world.

Other Map Boundaries

If you need to break boundaries down in other countries to the equivalent of US States, one resource is the Natural Earth coverages. These provide a detailed set of natural and geopolitical boundaries.

Cartopy has an interface for it cartopy.feature.NaturalEarthFeature and it's demonstrated below for administrative units below the country level (e.g., US/Mexican/Australian States, French Departments, UK Counties, New Zealand Regions, etc).

Below that example is one that goes down one more level, down to US Counties (and only US Counties).

... and for US counties, including covering for large lake bodies...

Version Information

There was once a very handy tool that would print version information of the Python version, operating system, and the versions of the Python packages you are running. It leveraged a "magic" command in IPython (which is what lies beneath Jupyter and JuptyerLab notebooks).

The developer has moved on to other things but the resource still has a narrow but ~militant~ ernest fan base (myself included). The original doesn't work with versions of Python above 3.7. So a couple of us wrote some patches to fix it. You can access my version below following these instructions.

  1. If you don't have GIT on your rig yet, you can fetch it via conda
!conda install -y -v git
  1. You can install it using the following command.
!pip install git+https://github.com/wjcapehart/version_information

JupyterLab Caveat

For people using Jupyter Lab, the interface does not play well with this "magic" function. It basically exports it as a JSON object with clicky expander buttons. However if you "Export" your notebook as an HTML or PDF file you should get a reasonable looking exported document.