Probably the best geospatial visualizations are done by overlaying the data over the map. Whether the whole globe, a continent, a state, or even the sky, it is one of the easiest ways for a viewer to comprehend the relationship between the data and the geography it has displayed.
In this recipe, you will learn how to project data on a map using matplotlib's Basemap
toolkit.
As we are already familiar with matplotlib as our plotting engine, we can extend that to matplotlib's capability to use other toolkits, one such example being the Basemap
mapping toolkit.
Basemap
itself doesn't do any plotting. It just transforms given geospatial coordinates to map projection and gives that data to matplotlib for plotting.
First, we need to install the Basemap
toolkit. If you are using EPD, Basemap
is already installed. If you are on Linux, it is best to use native package managers to install the package containing Basemap
. On Ubuntu, for example, the package is called python-mpltoolkits.basemap
and can be installed using standard package manager:
$ sudo apt-get install python-mpltoolkits.basemap
On Mac OS X, it is recommended to use EPD, although installation using popular package managers such as Homebrew, Fink, and pip is also possible.
Here is an example of how to use the Basemap
toolkit to plot simple Mercator projection within a specific region, specified by longitude, latitude coordinate pairs:
Basemap
defining the projection to be used (merc
for Mercator).Basemap
constructor) longitude and latitude for the lower-left and upper-right corners of a map.Basemap
instance map, to draw coastlines and countries.Basemap
instance map to fill continents and draw the map boundary.Basemap
instance map to draw meridians and parallels.The following code shows how to use Basemap
toolkit to plot a simple Mercator projection:
from mpl_toolkits.basemap import Basemap import matplotlib.pyplot as plt import numpy as np map = Basemap(projection='merc', resolution = 'h', area_thresh = 0.1, llcrnrlon=-126.619875, llcrnrlat=31.354158, urcrnrlon=-59.647219, urcrnrlat=47.517613) map.drawcoastlines() map.drawcountries() map.fillcontinents(color='coral', lake_color='aqua') map.drawmapboundary(fill_color='aqua') map.drawmeridians(np.arange(0, 360, 30)) map.drawparallels(np.arange(-90, 90, 30)) plt.show()
This will give a recognizable portion of our globe:
Now that we know how to plot a map, we need to know how to plot data on top of this map. If we recall that Basemap
is a big transcoder of longitude and latitude pairs into current map projections, we will recognize that all we need is a dataset that contains longitude and latitude that we can pass to Basemap
for projecting, before plotting over with matplotlib. We use the cities.shp
and cities.shx
files to load the coordinates of US cities and project them onto the map.
The file is provided in the Chapter06
folder of the code repository. Here's the example of how to achieve this:
from mpl_toolkits.basemap import Basemap import matplotlib.pyplot as plt import numpy as np map = Basemap(projection='merc', resolution = 'h', area_thresh = 100, llcrnrlon=-126.619875, llcrnrlat=25, urcrnrlon=-59.647219, urcrnrlat=55) shapeinfo = map.readshapefile('cities','cities') x, y = zip(*map.cities) # build a list of US cities city_names = [] for each in map.cities_info: if each['COUNTRY'] != 'US': city_names.append("") else: city_names.append(each['NAME']) map.drawcoastlines() map.drawcountries() map.fillcontinents(color='coral', lake_color='aqua') map.drawmapboundary(fill_color='aqua') map.drawmeridians(np.arange(0, 360, 30)) map.drawparallels(np.arange(-90, 90, 30)) # draw city markers map.scatter(x,y,25, marker='o',zorder=10) # plot labels at City coords. for city_label, city_x, city_y in zip(city_names, x, y): plt.text(city_x, city_y, city_label) plt.title('Cities in USA') plt.show()
The basics of Basemap
usage consists of importing the main module and instantiating a Basemap
class with desired properties. What we must specify during instantiations are the projections to be used and the portion of the globe that we want to work with.
Additional configuration can be applied before drawing the map and displaying the figure window with matplotlib.pyplot.show()
.
More than a dozen (or 32, to be precise) different projections are supported in Basemap
. Most of them are very narrow-usage oriented, but some are more general and apply to most common map visualizations.
We can easily see what projections are available by asking the Basemap module itself:
import mpl_toolkits.basemap print mpl_toolkits.basemap.supported_projections
|
McBryde-Thomas Flat-Polar Quartic |
|
Azimuthal Equidistant |
|
Sinusoidal |
|
Polyconic |
|
Oblique Mercator |
|
Gnomonic |
|
Mollweide |
|
Lambert Conformal |
|
Transverse Mercator |
|
North-Polar Lambert Azimuthal |
|
Gall Stereographic Cylindrical |
|
North-Polar Azimuthal Equidistant |
|
Miller Cylindrical |
|
Mercator |
|
Stereographic |
|
Equidistant Conic |
|
Cylindrical Equidistant |
|
North-Polar Stereographic |
|
South-Polar Stereographic |
|
Hammer |
|
Geostationary |
|
Near-Sided Perspective |
|
Eckert IV |
|
Albers Equal Area |
|
Kavrayskiy VII |
|
South-Polar Azimuthal Equidistant |
|
Orthographic |
|
Cassini-Soldner |
|
van der Grinten |
|
Lambert Azimuthal Equal Area |
|
South-Polar Lambert Azimuthal |
|
Robinson |
Usually, we plot the whole projection, if nothing is specified, and some reasonable defaults are used.
To zoom in on a specific region of the map, we specify the latitude and longitude of the lower-left and upper-right corners of the region you want to show. For this example, we will use the Mercator projection.
We have just scratched the surface of the capabilities of Basemap
toolkit. More examples can be found in the official documentation at http://matplotlib.org/basemap/users/examples.html.
Most of the data used in the examples in the official Basemap
documentation is located on remote servers and in a specific format. To efficiently fetch this data, NetCDF
data format is used. NetCDF
is a common data format designed with network efficiency in mind. It allows a program to fetch as much data as is needed, even when the whole dataset is very large, which makes using this format very practical. We don't have to download and store large datasets locally every time we want to use them and every time they change.
18.119.117.207