Plotting Charts with Images and Maps

As a developer with knowledge of Python you are already in a great position to start using data visualization. This superb cookbook shows you how in plain language and practical recipes, culminating with 3D animations.

(For more resources related to this topic, see here.)

This article explores how to work with images and maps. Python has some well-known image libraries that allow us to process images in both aesthetic and scientific ways.

We will touch on PIL's capabilities by demonstrating how to process images by applying filters and by resizing them.

Furthermore, we will show how to use image files as annotation for our matplotlibs' charts.

To deal with data visualization of geospatial datasets, we will cover the functionality of Python's available libraries and public APIs that we can use with map-based visual representations.

The final recipe shows how Python can create CAPTCHA test images.

Processing images with PIL

Why use Python for image processing, if we could use WIMP ( or WYSIWYG ( to achieve the same goal? This is used because we want to create an automated system to process images in real time without human support, thus, optimizing the image pipeline.

Getting ready

Note that the PIL coordinate system assumes that the (0,0) coordinate is in the upper-left corner.

The Image module has a useful class and instance methods to perform basic operations over a loaded image object (im):

  • im = This opens a file and loads the image into im object.
  • im.crop(box): This crops the image inside the coordinates defined by box. box defines left, upper, right, lower pixels coordinates (for example: box = (0, 100, 100,100)).
  • im.filter(filter): This applies a filter on the image and returns a filtered image.
  • im.histogram(): This returns a histogram list for this image, where each item represents the number of pixels. Number of items in the list is 256 for single channel images, but if the image is not a single channel image, there can be more items in the list. For an RGB image the list contains 768 items (one set of 256 values for each channel).
  • im.resize(size, filter): This resizes the image and uses a filter for resampling. The possible filters are NEAREST, BILINEAR, BICUBIC, and ANTIALIAS. The default is NEAREST.
  • im.rotate(angle, filter): This rotates an image in the counter clockwise direction.
  • im.split(): This splits bands of image and returns a tuple of individual bands. Useful for splitting an RGB image into three single band images.
  • im.transform(size, method, data, filter): This applies transformation on a given image using data and a filter. Transformation can be AFFINE, EXTENT, QUAD, and MESH. You can read more about transformation in the official documentation. Data defines the box in the original image where the transformation will be applied.

The ImageDraw module allows us to draw over the image, where we can use functions such as arc, ellipse, line, pieslice, point, and polygon to modify the pixels of the loaded image.

The ImageChops module contains a number of image channel operations (hence the name Chops) that can be used for image composition, painting, special effects, and other processing operations. Channel operations are allowed only for 8-bit images. Here are some interesting channel operations:

  • ImageChops.duplicate(image): This copies current image into a new image object
  • ImageChops.invert(image): This inverts an image and returns a copy
  • ImageChops.difference(image1, image2): This is useful for verification that images are the same without visual inspection

The ImageFilter module contains the implementation of the kernel class that allows the creation of custom convolution kernels. This module also contains a set of healthy common filters that allows the application of well-known filters (BLUR and MedianFilter) to our image.

There are two types of filters provided by the ImageFilter module: fixed image enhancement filters and image filters that require certain arguments to be defined; for example, size of the kernel to be used.

We can easily get the list of all fixed filter names in IPython:

In [1]: import ImageFilter In [2]: [ f for f in dir(ImageFilter) if f.isupper()] Out[2]: ['BLUR', 'CONTOUR', 'DETAIL', 'EDGE_ENHANCE', 'EDGE_ENHANCE_MORE', 'EMBOSS', 'FIND_EDGES', 'SHARPEN', 'SMOOTH', 'SMOOTH_MORE']

The next example shows how we can apply all currently supported fixed filters on any supported image:

import os import sys from PIL import Image, ImageChops, ImageFilter class DemoPIL(object): def __init__(self, image_file=None): self.fixed_filters = [ff for ff in dir(ImageFilter)
if ff.isupper()] assert image_file is not None assert os.path.isfile(image_file) is True self.image_file = image_file self.image = def _make_temp_dir(self): from tempfile import mkdtemp self.ff_tempdir = mkdtemp(prefix="ff_demo") def _get_temp_name(self, filter_name): name, ext = os.path.splitext(os.path.basename
(self.image_file)) newimage_file = name + "-" + filter_name + ext path = os.path.join(self.ff_tempdir, newimage_file) return path def _get_filter(self, filter_name): # note the use Python's eval() builtin here to return
function object real_filter = eval("ImageFilter." + filter_name) return real_filter def apply_filter(self, filter_name): print "Applying filter: " + filter_name filter_callable = self._get_filter(filter_name) # prevent calling non-fixed filters for now if filter_name in self.fixed_filters: temp_img = self.image.filter(filter_callable) else: print "Can't apply non-fixed filter now." return temp_img def run_fixed_filters_demo(self): self._make_temp_dir() for ffilter in self.fixed_filters: temp_img = self.apply_filter(ffilter) print "Images are in: {0}".format((self.ff_tempdir),) if __name__ == "__main__": assert len(sys.argv) == 2 demo_image = sys.argv[1] demo = DemoPIL(demo_image) # will create set of images in temporary folder demo.run_fixed_filters_demo()

We can run this easily from the command prompt:

$ image.jpeg

We packed our little demo in the DemoPIL class, so we can extend it easily while sharing the common code around the demo function run_fixed_filters_demo. Common code here includes opening the image file, testing if the file is really a file, creating temporary directory to hold our filtered images, building the filtered image filename, and printing useful information to user. This way the code is organized in a better manner and we can easily focus on our demo function, without touching other parts of the code.

This demo will open our image file and apply every fixed filter available in ImageFilter to it and save that new filtered image in a unique temporary directory. The location of this temporary directory is retrieved, so we can open it with our OS's file explorer and view the created images.

As an optional exercise, try extending this demo class to perform other filters available in ImageFilter on the given image.

How to do it...

The example in this section shows how we can process all the images in a certain folder. We specify a target path, and the program reads all the image files in that target path (images folder) and resizes them to a specified ratio (0.1 in this example), and saves each one in a target folder called thumbnail_folder:

import os import sys from PIL import Image class Thumbnailer(object): def __init__(self, src_folder=None): self.src_folder = src_folder self.ratio = .3 self.thumbnail_folder = "thumbnails" def _create_thumbnails_folder(self): thumb_path = os.path.join(self.src_folder,
self.thumbnail_folder) if not os.path.isdir(thumb_path): os.makedirs(thumb_path) def _build_thumb_path(self, image_path): root = os.path.dirname(image_path) name, ext = os.path.splitext(os.path.basename(image_path)) suffix = ".thumbnail" return os.path.join(root, self.thumbnail_folder,
name + suffix + ext) def _load_files(self): files = set() for each in os.listdir(self.src_folder): each = os.path.abspath(self.src_folder + '/' + each) if os.path.isfile(each): files.add(each) return files def _thumb_size(self, size): return (int(size[0] * self.ratio), int(size[1] * self.ratio)) def create_thumbnails(self): self._create_thumbnails_folder() files = self._load_files() for each in files: print "Processing: " + each try: img = thumb_size = self._thumb_size(img.size) resized = img.resize(thumb_size, Image.ANTIALIAS) savepath = self._build_thumb_path(each) except IOError as ex: print "Error: " + str(ex) if __name__ == "__main__": # Usage: # my_images assert len(sys.argv) == 2 src_folder = sys.argv[1] if not os.path.isdir(src_folder): print "Error: Path '{0}' does not exits.".format((src_folder)) sys.exit(-1) thumbs = Thumbnailer(src_folder) # optionally set the name of theachumbnail folder relative
to *src_folder*. thumbs.thumbnail_folder = "THUMBS" # define ratio to resize image to # 0.1 means the original image will be resized to
10% of its size thumbs.ratio = 0.1 # will create set of images in temporary folder thumbs.create_thumbnails()

How it works...

For the given src_folder folder, we load all the files in this folder and try to load each file using; this is the logic of the create_thumbnails() function. If the file we try to load is not an image, IOError will be thrown, and it will print this error and skip to next file in the sequence.

If we want to have more control over what files we load, we should change the _load_files() function to only include files with certain extension (file type):

for each in os.listdir(self.src_folder): if os.path.isfile(each) and os.path.splitext(each)
is in ('.jpg','.png'): self._files.add(each)

This is not foolproof, as file extension does not define file type, it just helps the operating system to attach a default program to the file, but it works in majority of the cases and is simpler than reading a file header to determine the file content (which still does not guarantee that the file really is the first couple of bytes, say it is).

There's more...

With PIL, although not used very often, we can easily convert images from one format to the other. This is achievable with two simple operations: first open an image in a source format using open(), and then save that image in the other format using save(). Format is defined either implicitly via filename extension (.png or .jpeg), or explicitly via the format of the argument passed to the save() function.

Plotting with images

Images can be used to highlight the strengths of your visualization in addition to pure data values. Many examples have proven that by using symbolic images, we map deeper into the viewer's mental model, thereby helping the viewer to remember the visualizations better and for a longer time. One way to do so is to place images where your data is, to map the values to what they represent. The matplotlib library is capable of delivering this functionality, thus we will demonstrate how to do it.

Getting ready

Use the fictional example from the story The Gospel of the Flying Spaghetti Monster , by Bobby Henderson where the author correlates number of pirates with sea-surface temperature. To highlight this correlation, we will display the size of the pirate ship proportional to the value representing the number of pirates in the year the sea-surface temperature is measured.

We will use Python matplotlib library's ability to annotate using images and text with advanced location settings, as well as arrow capabilities.

All the files required in the following recipe are available in the source code repository in the ch06 folder.

How to do it...

The following example shows how to add an annotation to a chart using images and text:

import matplotlib.pyplot as plt from matplotlib._png import read_png from matplotlib.offsetbox import TextArea, OffsetImage, \ AnnotationBbox def load_data(): import csv with open('pirates_temperature.csv', 'r') as f: reader = csv.reader(f) header = datarows = [] for row in reader: datarows.append(row) return header, datarows def format_data(datarows): years, temps, pirates = [], [], [] for each in datarows: years.append(each[0]) temps.append(each[1]) pirates.append(each[2]) return years, temps, pirates

After we have defined helper functions, we can approach the construction of the figure object and add subplots. We will annotate these for every year in the collection of years using the image of the ship, scaling the image to the appropriate size:

if __name__ == "__main__": fig = plt.figure(figsize=(16,8)) ax = plt.subplot(111) # add sub-plot header, datarows = load_data() xlabel, ylabel = header[0], header[1] years, temperature, pirates = format_data(datarows) title = "Global Average Temperature vs. Number of Pirates" plt.plot(years, temperature, lw=2) plt.xlabel(xlabel) plt.ylabel(ylabel) # for every data point annotate with image and number for x in xrange(len(years)): # current data coordinate xy = years[x], temperature[x] # add image ax.plot(xy[0], xy[1], "ok") # load pirate image pirate = read_png('tall-ship.png') # zoom coefficient (move image with size) zoomc = int(pirates[x]) * (1 / 90000.) # create OffsetImage imagebox = OffsetImage(pirate, zoom=zoomc) # create anotation bbox with image and setup properties ab = AnnotationBbox(imagebox, xy, xybox=(-200.*zoomc, 200.*zoomc), xycoords='data', boxcoords="offset points", pad=0.1, arrowprops=dict(arrowstyle="->", connectionstyle="angle,angleA=0,
angleB=-30,rad=3") ) ax.add_artist(ab) # add text no_pirates = TextArea(pirates[x], minimumdescent=False) ab = AnnotationBbox(no_pirates, xy, xybox=(50., -25.), xycoords='data', boxcoords="offset points", pad=0.3, arrowprops=dict(arrowstyle="->", connectionstyle="angle,angleA=0,
angleB=-30,rad=3") ) ax.add_artist(ab) plt.grid(1) plt.xlim(1800, 2020) plt.ylim(14, 16) plt.title(title)

The preceding code should give the following plot:

How it works...

We start by creating a figure of a decent size, that is, 16 x 8. We need this size to fit the images we want to display. Now, we load our data from the file, using the csv module. Instantiating the csv reader object, we can iterate over the data from the file row by row. Note how the first row is special, it is the header describing our columns. As we have plotted years on the x axis and temperature on the y axis, we read that:

xlabel, ylabel, _ = header

And use the following lines:

plt.xlabel(xlabel) plt.ylabel(ylabel)

We used neat Python convention here to unpack the header into three variables, where by using _ for variable name, we indicate that we are not interested in the value of that variable.

We return the header and datarows lists from the load_data function to the main caller.

Using the format_data() function, we read every item in the list, and add each separate entity (year, temperature, and number of pirates) into the relevant ID list for that entity.

Year is displayed along the x axis, while temperature is on the y axis. The number of pirates is displayed as an image of a pirate ship, and also to add precision, the value will be displayed.

We plot year/temperature values using the standard plot() function, not adding anything more, apart from making the line a bit wider (2 pt).

We proceed then to add one image for every measurement and to illustrate the number of pirates for a given year. For this we loop over the range of values of length (range(len(years))) , plotting one black point on each year/temperature coordinate:

ax.plot(xy[0], xy[1], "ok")

The image of the ship is loaded from file into a suitable array format using the read_png helper function:

pirate = read_png('tall-ship.png')

We then compute zoom coefficient (zoomc) to enable us to scale the size of the image in proportion to the number of pirates for the current (pirates[x]) measurement. We also use the same coefficient to position the image along the plot.

The actual image is then instantiated inside OffsetImage—the image container with relative position to its parent (AnnotationBbox).

AnnotationBbox is an annotation-like class, but instead of displaying just text as with the Axes.annotate function, it can display other OffsetBox instances. This allows us to load an image or text object in an annotation and locate it at a particular distance from the data point, as well as to use the arrowing capabilities (arrowprops) to precisely point to an annotated data point.

We supply the AnnotateBbox constructor with certain arguments:

  • Imagebox: This must be an instance of OffsetBox (for example, OffsetImage); it is the content of the annotation box
  • xy: This is the data point coordinate that the annotation relates to
  • xybox: This defines the location of the annotation box
  • xycoords: This defines what coordinating system is used by xy (for example, data coordinates)
  • boxcoords: This defines what coordinating system is used by xybox (for example, offset from the xy location)
  • pad: This specifies the amount of padding
  • arrowprops: This is the dictionary of properties for drawing an arrow connection from an annotation-bounding box to a data point

We add text annotation to this plot, using the same data items from the pirates list, with a slightly different relative position. Most of the arguments of the second AnnotationBbox are the same—we adjust xybox and pad to locate the text to the opposite side of the line. The text is inside the TextArea class instance, this is similar to what we do with image, but with text time.TextArea and OffsetImage inherit from the same parent class, OffsetBox.

We set the text in this TextArea instance to no_pirates and put it in our AnnotationBbox.

Displaying an image with other plots in the figure

This recipe will show how we can make simple yet effective usage of Python matplotlib library to process image channels and display per-channel histogram of an external image.

Getting ready

We have provided some sample images, but the code is ready to load any image file, provided it is supported by matplotlib's imread function.

In this recipe, we will learn how to combine different matplotlib plots to achieve functionality of a simple image viewer that displays an image histogram for red, green, and blue channels.

How to do it...

To show how to build an image histogram viewer, we are going to implement a simple class named ImageViewer and that class will contain helper methods to:

  1. Load image.
  2. Separate RGB channels from image matrix.
  3. Configure figure and axes (subplots).
  4. Plot channel histograms.
  5. Plot the image.

The following code shows how to build an image histogram viewer:

import matplotlib.pyplot as plt import matplotlib.image as mplimage import matplotlib as mpl import os class ImageViewer(object): def __init__(self, imfile): self._load_image(imfile) self._configure() self.figure = plt.gcf() t = "Image: {0}".format(os.path.basename(imfile)) self.figure.suptitle(t, fontsize=20) self.shape = (3, 2) def _configure(self): mpl.rcParams['font.size'] = 10 mpl.rcParams['figure.autolayout'] = False mpl.rcParams['figure.figsize'] = (9, 6) mpl.rcParams[''] = .9 def _load_image(self, imfile): = mplimage.imread(imfile) @staticmethod def _get_chno(ch): chmap = {'R': 0, 'G': 1, 'B': 2} return chmap.get(ch, -1) def show_channel(self, ch): bins = 256 ec = 'none' chno = self._get_chno(ch) loc = (chno, 1) ax = plt.subplot2grid(self.shape, loc) ax.hist([:, :, chno].flatten(), bins, color=ch, ec=ec,\ label=ch, alpha=.7) ax.set_xlim(0, 255) plt.setp(ax.get_xticklabels(), visible=True) plt.setp(ax.get_yticklabels(), visible=False) plt.setp(ax.get_xticklines(), visible=True) plt.setp(ax.get_yticklines(), visible=False) plt.legend() plt.grid(True, axis='y') return ax def show(self): loc = (0, 0) axim = plt.subplot2grid(self.shape, loc, rowspan=3) axim.imshow( plt.setp(axim.get_xticklabels(), visible=False) plt.setp(axim.get_yticklabels(), visible=False) plt.setp(axim.get_xticklines(), visible=False) plt.setp(axim.get_yticklines(), visible=False) axr = self.show_channel('R') axg = self.show_channel('G') axb = self.show_channel('B') if __name__ == '__main__': im = 'images/yellow_flowers.jpg' try: iv = ImageViewer(im) except Exception as ex: print ex

How it works...

Reading from the end of the code, we see hard-coded filenames. These can be swapped by loading the argument from command line and parsing the given argument into the im variable using the sys.argv sequence.

We instantiate the ImageViewer class with the provided path to an image file. During object instantiation, we try to load an image file into an array, configure the figure via the rcParams dictionary, set the figure size and title, and define object fields (self.shape) to be used inside object's methods.

The main method here is show(), which creates a layout for the figure and loads the image arrays into the main (left column) subplot. We hide any ticks and tick labels as this is the actual image, where we don't have to use the ticks.

We then call the private method show_channel() for each of the red, green, and blue channels. This method also creates new subplot axes, this time in the right-hand side column, with each one in separate row. We plot the histogram for each channel in a separate subplot.

We also set up a little plot to remove unnecessary x ticks and add a legend in case we want to print this figure in a non-color environment. Therefore, we could discern channel representation even in those environments.

After we run this code we will get the following screenshot:

There's more...

The use of histogram plot type is just a choice for this image viewer example. We could have used any of the matplotlib supported plot types. Another real-world example would be to plot EEG or similar medical records where we would want to display slice as an image, the time series of EEG recorded as a line plot, and also additional meta information about the data shown, that would probably go into matplotlib.text.Text artists.

Having the ability to interact with the user GUI event, matplotlib's figure allows us also to implement interaction where we would want to zoom into all plots if we manually zoom on one plot only. That would be another usage where we want to display an image and zoom into it while also zoom into other displayed plots in the currently active figure. An idea would be to use motion_notify_event to call a function that will update x and y limits for all axes (subplots) in the current figure.

Plotting data on a map using Basemap

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 relation between the data and geography it has displayed.

In this recipe we will be learning how to project data on a map using matplotlib's Basemap toolkit.

Getting ready

As we are already familiar with matplotlib as our plotting engine, we can extend that to matplotlib's capabilities 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.

How to do it...

Here is a example on how to use the Basemap toolkit to plot simple Mercator projection within a specific region, specified by long, lat coordinate pairs:

  1. We instantiate Basemap defining the projection to be used (merc for Mercator).
  2. We define (in the same Basemap constructor) longitude and latitude for the lower-left and upper-right corners of a map.
  3. We set up the Basemap instance map, to draw coastlines and countries.
  4. We set up the Basemap instance map to fill continents and draw the map boundary.
  5. We instruct the 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))

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 recognize that all we need is a dataset that contains long/lat that we 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 on to the map. The file is provided in the ch06 folder of the code repository. Here's the example on 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')

How it works...

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 a globe that we want to work with.

Additional configuration can be applied before drawing the map and displaying the figure window with

More than a dozen (or 32, to be precise) of different projections are supported in Basemap. Most of them are very narrow-usage oriented, but some are more general and applied for most common map visualizations.

We can easily see what projections are available by asking the Basemap module itself:

In [5]: import mpl_toolkits.basemap In [6]: print mpl_toolkits.basemap.supported_projections mbtfpq McBryde-Thomas Flat-Polar Quartic aeqd Azimuthal Equidistant sinu Sinusoidal poly Polyconic omerc Oblique Mercator gnom Gnomonic moll Mollweide lcc Lambert Conformal tmerc Transverse Mercator nplaea North-Polar Lambert Azimuthal gall Gall Stereographic Cylindrical npaeqd North-Polar Azimuthal Equidistant mill Miller Cylindrical merc Mercator stere Stereographic eqdc Equidistant Conic cyl Cylindrical Equidistant npstere North-Polar Stereographic spstere South-Polar Stereographic hammer Hammer geos Geostationary nsper Near-Sided Perspective eck4 Eckert IV aea Albers Equal Area kav7 Kavrayskiy VII spaeqd South-Polar Azimuthal Equidistant ortho Orthographic cass Cassini-Soldner vandg van der Grinten laea Lambert Azimuthal Equal Area splaea South-Polar Lambert Azimuthal robin Robinson

Usually, we will plot the whole projection, if nothing is specified, some reasonable defaults are used.

To zoom in on a specific region of the map, we will 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.

Here we see how the arguments names are shortened descriptions:

  • llcrnrlon: This is lower-left corner longitude
  • llcrnrlat: This is lower-left corner latitude
  • urcrnrlon: This is upper-right corner longitude
  • urcrnrlat: This is upper-right corner latitude

There's more...

We have just scratched the surface of the capabilities of Basemap toolkit, more examples can be found in the official documentation at

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.

Plotting data on a map using Google Map API

In this recipe, we will diverge from the desktop environment and show how we can output for the Web. Although, the main language for the web frontend is not Python but HTML, CSS, and JavaScript, we can still use Python for heavy lifting: fetch data, process it, perform intensive computations, and render data in a format(s) suitable for web output, that is, create HTML pages with the required JavaScript version to render our visualization(s).

Getting ready

We will use Google Data Visualization Library for Python to help us prepare data for the frontend interface, where we will use another Google Visualization API to render data in the desired visualization, that is, a map and a table.

Before we start, we need to install the google-visualization-python module. Download the latest stable version from, unpack the archive and install the module. The following actions demonstrate how to do this:

$ tar xfv gviz_api_py-1.8.2.tar.gz $ cd gviz_api_py $ sudo python ./ install


In this article, we covered image processing and projecting data onto maps. We saw that Python has some well-known image libraries that allow us to process images in both aesthetic and scientific ways. We also touched on PIL's capabilities by demonstrating how to process images by applying filters and by resizing them.

Resources for Article:

Further resources on this subject:

Books to Consider

comments powered by Disqus

An Introduction to 3D Printing

Explore the future of manufacturing and design  - read our guide to 3d printing for free