COMP0023: Research Software Engineering With Python


Recap: Understanding the "Greengraph" Example

We now know enough to understand everything we did in the initial example chapter on the "Greengraph" (notebook). Go back to that part of the notes, and re-read the code.

Now, we can even write it up into a class, and save it as a module. Remember that it is generally a better idea to create files in an editor or integrated development environment (IDE) rather than through the notebook!

Classes for Greengraph

The original example was written as a collection of functions. Alternatively, we can rewrite it in an object-oriented style, using classes to group related functionality.

In [1]:
mkdir -p greengraph  # Create the folder for the module (on mac or linux)
In [2]:
%%writefile greengraph/graph.py
import numpy as np
import geopy
from .map import Map

class Greengraph(object):
    def __init__(self, start, end):
        self.start = start
        self.end = end
        self.geocoder = geopy.geocoders.Yandex(lang="en_US")

    def geolocate(self, place):
        return self.geocoder.geocode(place, exactly_one=False)[0][1]

    def location_sequence(self, start, end, steps):
        lats = np.linspace(start[0], end[0], steps)
        longs = np.linspace(start[1], end[1], steps)
        return np.vstack([lats, longs]).transpose()

    def green_between(self, steps):
        return [Map(*location).count_green()
                for location in self.location_sequence(
Writing greengraph/graph.py
In [3]:
%%writefile greengraph/map.py

import numpy as np
from io import BytesIO
import imageio as img
import requests

class Map(object):
    def __init__(self, lat, long, satellite=True, zoom=10,
                 size=(400, 400), sensor=False):
        base = "https://static-maps.yandex.ru/1.x/?"

        params = dict(
            size=str(size[0]) + "," + str(size[1]),
            ll=str(long) + "," + str(lat),
            l="sat" if satellite else "map",

        self.image = requests.get(
            base, params=params).content  # Fetch our PNG image data
        content = BytesIO(self.image)
        self.pixels = img.imread(content) # Parse our PNG image as a numpy array
    def green(self, threshold):
        # Use NumPy to build an element-by-element logical array
        greener_than_red = self.pixels[:, :, 1] > threshold * self.pixels[:, :, 0]
        greener_than_blue = self.pixels[:, :, 1] > threshold * self.pixels[:, :, 2]
        green = np.logical_and(greener_than_red, greener_than_blue)
        return green

    def count_green(self, threshold=1.1):
        return np.sum(self.green(threshold))

    def show_green(data, threshold=1.1):
        green = self.green(threshold)
        out = green[:, :, np.newaxis] * array([0, 1, 0])[np.newaxis, np.newaxis, :]
        buffer = BytesIO()
        result = img.imwrite(buffer, out, format='png')
        return buffer.getvalue()
Writing greengraph/map.py
In [4]:
%%writefile greengraph/__init__.py
from .graph import Greengraph
Writing greengraph/__init__.py

Invoking our code and making a plot

In [5]:
from matplotlib import pyplot as plt
from greengraph import Greengraph
%matplotlib inline

mygraph = Greengraph('New York', 'Chicago')
data = mygraph.green_between(20)
TypeError                                 Traceback (most recent call last)
Cell In[5], line 5
      2 from greengraph import Greengraph
      3 get_ipython().run_line_magic('matplotlib', 'inline')
----> 5 mygraph = Greengraph('New York', 'Chicago')
      6 data = mygraph.green_between(20)

File ~/work/rsd-engineeringcourse/rsd-engineeringcourse/ch01data/greengraph/graph.py:10, in Greengraph.__init__(self, start, end)
      8 self.start = start
      9 self.end = end
---> 10 self.geocoder = geopy.geocoders.Yandex(lang="en_US")

TypeError: __init__() got an unexpected keyword argument 'lang'
In [6]:
NameError                                 Traceback (most recent call last)
Cell In[6], line 1
----> 1 plt.plot(data)

NameError: name 'data' is not defined