Tags

, , , , , , , , , , , , ,

Arkwood is going home to visit his parents. ‘What’s the weather like in Belgium? I need to know,’ he asked of me. I did not know. But luckily, my robots do.

I have added a new feature to SaltwashAR – a Python Augmented Reality application – so that the robot can fetch a weather report from anywhere in the world. Better still, if the weather is cloudy then the robot is enveloped in fog. Just to be clear that it is cloudy, so to speak.

Here’s Rocky Robot in action, providing the current weather for far-flung places:

Hurray! I ask for the weather in Hong Kong and the robot tells me that there is patchy rain nearby. In the United Arab Emirates, the weather is clear. But the robot is almost lost in the Indian fog!

So how the hell does the new Weather feature work? Let’s take a look at the first half of the Python class:

from features.base import Feature, Speaking
from OpenGL.GL import *
from urllib2 import urlopen
import json
from time import sleep

class Weather(Feature, Speaking):

    # Attention: visit https://www.worldweatheronline.com/ to register for a free API key
    
    WEATHER_API_KEY = None 
    WEATHER_API = 'http://api.worldweatheronline.com/free/v1/weather.ashx?q={}&format=json&num_of_days=1&key={}'
    CLOUDY_TERMS = ['partly cloudy', 'mist', 'fog', 'overcast']

    def __init__(self, text_to_speech, speech_to_text):
        Feature.__init__(self)
        Speaking.__init__(self, text_to_speech)
        self.speech_to_text = speech_to_text
        self.is_cloudy = False

    # start thread
    def start(self, args=None):
        Feature.start(self, args)
 
        # enable fog if cloudy
        if self.is_cloudy:
            glFogi(GL_FOG_MODE, GL_LINEAR)
            glFogfv(GL_FOG_COLOR, (0.5, 0.5, 0.5, 1.0))
            glFogf(GL_FOG_DENSITY, 0.35)
            glHint(GL_FOG_HINT, GL_NICEST)
            glFogf(GL_FOG_START, 1.0)
            glFogf(GL_FOG_END, 5.0)
            glEnable(GL_FOG)
        else:
            glDisable(GL_FOG)

    # stop thread
    def stop(self):
        Feature.stop(self)
        
        # disable fog
        glDisable(GL_FOG)
        self.is_cloudy = False

The Weather feature inherits from the Feature base class, which provides threading (all features run in threads so as not to block the main application process from rendering to screen). We also inherit from the Speaking base class, to let the robot’s mouth move when she speaks.

You will notice that the class has a constant WEATHER_API_KEY = None. If you want to use the Weather feature, you will need to obtain a free API key from World Weather Online. Simply paste the API key in place of None, and you’re good to go!

The __init__ method is passed Text To Speech and Speech To Text parameters, so that we will be able to communicate with the robot. We also have a is_cloudy class variable, which will act as a flag for cloudy weather.

Next up, we are overriding the Feature base class start method, which starts our thread. Why are we overriding it? you ask me. Well, I want to render some OpenGL fog to the screen if the is_cloudy flag is set to True, but this cannot happen inside a thread (it is not the correct context for OpenGL commands). So we hijack the start method for our fog effect.

We are also overriding the thread stop method, to kill the fog effect.

Okay, now let’s take a gander at the second half of our Weather class:

# run thread
def _thread(self, args):
   
    # check for API key
    if not self.WEATHER_API_KEY:
        self._text_to_speech("You need to visit world weather online to register for a free API key")
        return
        
    # ask user for location
    self._text_to_speech("Which location would you like weather from?")

    # user replies with location
    location = self.speech_to_text.convert()
    if not location: return

    # fetch weather 
    weather_description = None
    url = self.WEATHER_API.format(location.replace (' ', '+'), self.WEATHER_API_KEY)

    try:
        response = urlopen(url)
        object = json.load(response)
        weather_description = object['data']['current_condition'][0]['weatherDesc'][0]['value'].lower()
    except:
        print "Error getting weather"

    if not weather_description: return   

    # check whether to stop thread
    if self.is_stop: return

    # inform user of weather
    weather_report = 'The weather in {} is {}.'.format(location, weather_description)

    if weather_description in self.CLOUDY_TERMS:
        self.is_cloudy = True
        self._text_to_speech(weather_report + " It's real cloudy here.")
    else:
        self.is_cloudy = False
        self._text_to_speech(weather_report)

    sleep(4)

First, we check that an API key from World Weather Online is in place, as mentioned above – if it’s not, then the robot will get cross with you!

Next, the robot uses Text To Speech through the computer speakers to ask you for a location from which to fetch weather.

Using Speech To Text through the computer microphone, you provide a location. For example, you say “Hong Kong”.

Armed with your location, the robot uses the World Weather Online service to retrieve the current weather as a chunk of JSON. Notice how the service url has the API key injected into it, along with the correct formatting of the location e.g. ‘Hong+Kong’.

The JSON is then interrogated for the current weather description, which for Hong Kong is ‘patchy rain nearby’.

Okay, now we check whether to stop the thread. Why? Well, if the robot is no longer in front of the webcam then it’s not going to be able to furnish us with the weather report.

Lastly, the robot uses Text To Speech through the computer speakers to tell us the weather report for Hong Kong. If the weather description we obtained from the World Weather Online service is in our CLOUDY_TERMS list, we set our is_cloudy flag – which means that the robot will be shrouded in thick fog. How very apt!

And that’s it. The thread sleeps for a few seconds before beginning again.

Note: be sure to check the World Weather Online Free Weather API Terms of Use before making requests.

I told Arkwood that Belgium was ‘clear’. Not a cloud in sight!

‘Ah, damn!’ he said, packing a suitcase. ‘That means I’ll have to do shit with the old farts, like go out cycling and mountain treks.’ Charming.

Ciao!

Please check out the SaltwashAR Wiki for details on how to install and help develop the SaltwashAR Python Augmented Reality application.

Advertisements