Professional Documents
Culture Documents
Dress For The Weather: Projects
Dress For The Weather: Projects
Projects
Python
In this resource you’re going to write a program that will allow a user to type in a city
they’re travelling to, and the date they’ll be arriving. The program will then use open
data (https://en.wikipedia.org/wiki/Open_data) from
OpenWeatherMap (http://openweathermap.org/api) to �nd out what the weather
is like in that city at that time, and advise the user on what they should wear when
they arrive.
By creating the Dress for the Weather project you will learn:
This resource covers elements from the following strands of the Raspberry Pi Digital
Making Curriculum (https://www.raspberrypi.org/curriculum/):
1 of 19 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
In this resource you’re going to write a program that will allow a user to type in a city
they’re travelling to, and the date they’ll be arriving. The program will then use open
data (https://en.wikipedia.org/wiki/Open_data) to �nd out what the weather is
like in that city at that time, and advise the user on what they should wear when they
arrive.
The �rst thing you’ll need to do is to get access to the weather forecast data. We can
get the data from a site called OpenWeatherMap (http://openweathermap.org
/api).
Once signed in, you can see your API key on the
dashboard (http://home.openweathermap.org/):
2 of 19 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
You’ll need a place to save your �les in for this project. In your home directory,
create a new directory called dress-for-the-weather . You can do this with
the File Explorer, or by opening a terminal window and typing:
mkdir dress-for-the-weather
Now, open up a new Python 3 �le in your favourite editor; for instance, you can
navigate to Menu > Programming > Python3. Create a new �le (File > New
Window) and save this as weather.py in the new directory.
You’re going to need a few modules to complete this project, so you can import
them by writing the following lines of code at the top of your �le:
Next, get your API key for OpenWeatherMap and declare it as a variable in your
program:
The OpenWeatherMap API requires you to use a city ID to identify the di�erent
cities all around the world. They provide a list of city IDs in the following linked
�le (http://bulk.openweathermap.org/sample/city.list.json.gz). Download the
3 of 19 �le and place it in the same directory as your code. 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
The �le is compressed, so if you want to read the data you’ll need to decompress
it. Open a terminal and go to the dress-for-the-weather directory:
cd dress-for-the-weather
ls
gunzip city.list.json.gz
If you list the directory contents again, you should see a new �le called
city.list.json . Don’t try and open the �le; it’s really big, and could crash
your computer.
To get the city that the person is travelling to, you �rst need to understand the
contents of the �le you have just downloaded. You can explore the �le using the shell.
Save and run your weather.py �le ( ctrl+s and then F5 in IDLE). In the shell,
type the following command:
cities = open('city.list.json').read()
This has loaded all the lines of the �le into a huge string called cities .
To have a look at some of the items in the list, you can type the following in the
shell:
cities[0:130]
Now that you understand the nature of the JSON �le, you can use it to �nd the
id of any city in there. You can write a new function to do this, so switch back
over to your weather.py �le and add the following:
def get_city_id():
The �rst thing to do is to load all of those json objects, convert each one to a
dictionary, and then add them all to a huge list called data :
def get_city_id():
with open('city.list.json') as f:
data = loads(f.read())
Next, you need to ask the user where they’re travelling to. Just in case their city is
not in the list, we’ll set a variable called city_id to False as well:
def get_city_id():
with open('city.list.json') as f:
data = loads(f.read())
city = input('Which is the closest city to the place you are
travelling to?' )
city_id = False
Now your program needs to iterate over every dictionary in that list, and see if the
city the user typed in is there:
def get_city_id():
5 of 19 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
with open('city.list.json') as f:
data = loads(f.read())
city = input('Which is the closest city to the place you are
travelling to?' )
city_id = False
for item in data:
if item['name'] == city:
city_id = item['id']
return city_id
Run your code, and then move over into the shell to test it:
get_city_id()
Do you notice anything strange when you test the code with the city
Peterborough?
>>> get_city_id()
Which is the closest city to the place you are travelling to?
Peterborough
5091002
The ID 5091002 is di�erent to the ID 2640354 you found earlier! Why could this
be? You can alter your code so that it prints the country name, to try and debug
the code:
def get_city_id():
with open('city.list.json') as f:
data = loads(f.read())
city = input('Which is the closest city to the place you are
travelling to? ')
city_id = False
for item in data:
if item['name'] == city:
city_id = item['id']
print(item['country'])
return city_id
>>> get_city_id()
Which is the closest city to the place you are travelling to?
Peterborough
6 of 19 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
GB
CA
AU
AU
US
5091002
def get_city_id():
with open('city.list.json') as f:
data = loads(f.read())
city = input('Which is the closest city to the place you are
travelling to?' )
city_id = False
for item in data:
if item['name'] == city:
answer = input('Is this in ' + item['country'])
if answer == 'y':
city_id = item['id']
break
return city_id
If the city hasn’t been found then the city_id will still be set to False , so you
can tell the user their city wasn’t found and exit the program. The �nished
function should look like this:
def get_city_id():
with open('city.list.json') as f:
data = loads(f.read())
city = input('Which is the closest city to the place you are
travelling to?' )
city_id = False
for item in data:
if item['name'] == city:
answer = input('Is this in ' + item['country'])
if answer == 'y':
city_id = item['id']
break
if not city_id:
7 of 19 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
return city_id
Now that you can get the correct city ID, you have everything you need to get the
weather for that location from the OpenWeatherMap API. You can get the data using
a simple web request. The request you need to make must include the city_id and
your key, placed where the # symbols are in the example below.
http://api.openweathermap.org/data/2.5/forecast?id=######&
APPID=################
For instance, to get the weather forecast for Peterborough, you could simply copy
and paste the web address below in the URL bar of your browser, replacing the fake
key at the end with your actual key.
http://api.openweathermap.org/data/2.5/forecast?id=2640354&
APPID=123456789abcdefghijklmnopqrstuvw
This might look a little confusing, but with Python you can easily examine the data.
The get method that you have already imported from the requests module is
all you need to access data from the web. Start by de�ning a new function that
takes the city_id as an argument:
8 of 19 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
def get_weather_data(city_id):
def get_weather_data(city_id):
weather_data = get('http://api.openweathermap.org/data/2.5
/forecast?id={}&APPID={}'.format(city_id, KEY))
Here the curly brackets {} within the URL are replaced with whatever is in the
brackets after .format .
The data that your program downloads is just a long string. You can convert it to
JSON easily enough, though, and return it:
def get_weather_data(city_id):
weather_data = get('http://api.openweathermap.org/data/2.5
/forecast?id={}&APPID={}'.format(city_id, KEY))
return weather_data.json()
To use the data you’ve just downloaded, you’ll need to understand its nature.
Save and run your code, then move into the shell and type the following:
weather = get_weather_data('2640354')
To look at the data, you can type the following into the shell:
weather
That’s quite a big dictionary and very confusing to look at. Fortunately, you’ve
imported the pretty print method called pprint that will help make more sense
of the data:
pprint(weather)
9 of 19 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
The dictionary has a key in it called list on line 9. You can look at this section of
the dictionary by typing the following:
pprint(weather['list'])
That’s still pretty big, so have a look at the zero-th item in the list:
pprint(weather['list'][0])
10 of 19 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
It will be di�erent for you, as you are accessing the weather forecast at a di�erent
time.
What you have here is a dictionary containing weather data. This dictionary has a
few keys in it, but for now the most important one is dt_txt . You can look at this
by typing the following:
weather['list'][0]['dt_txt']
weather['list'][1]['dt_txt']
You should see that the two strings returned are dates and times that are three
hours apart. That is what list contains: a list of predicted weather data for 5
days, each three hours apart. So to know what the user should wear, you’re going
to need the time and date they’re getting to the city.
11 of 19 In this continuation from the previous worksheet (worksheet.md), you will learn 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
The date and time from the JSON �le is in a speci�c format, YYYY-MM-DD
HH:00:00 , so you need to ask the user what day and hour they intend to arrive at
the city, and then convert it into this format.
def get_arrival():
Now you can use the datetime method you imported to get the current date
and time:
def get_arrival():
today = datetime.now()
To have the user choose a date, you need to give them a range of dates to choose
from. As this is a 5-day forecast, it will range from the date today up to the date in
4 days’ time. To get the date in 4 days’ time, you can use the timedelta
method:
Next, you need to �nd out when the user plans on arriving at their destination,
and give them a choice of dates. The strftime method will let you print out
speci�c dates in a month:
Now the same needs to be done for the time the user is planning on arriving. The
forecasts are only once every 3 hours, starting at 00:00:00. To calculate the time
here, you can use the modulus ( % ) operator, which will get the remainder from a
12 of 19 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
division:
To �nish o�, the date and time they are arriving can be converted to the same
format that’s used in the JSON �le:
With the arrival date and time returned, the complete function should look like
this:
def get_arrival():
today = datetime.now()
max_day = today + timedelta(days = 4)
print('What day of the month do you plan to arrive at your
destination?')
print(today.strftime('%d'), '-', max_day.strftime('%d'))
day = input()
print('What hour do you plan to arrive?')
print('0 - 24')
hour = int(input())
hour = hour - hour % 3
arrival = today.strftime('%Y') + '-' + today.strftime('%m') + '-'
+ day + ' ' + str(hour) + ':00:00'
return arrival
Test the function by running the code and then typing the following into the shell:
get_arrival()
Step 13 Retrieving the forecast for the required date and time
Now that you have the date and time of arrival, you can query the dictionary for the
13 of 19 correct forecast. 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
Start by de�ning a new function that takes the weather_data and the
arrival as arguments:
Now you can iterate over the weather_data['list] to �nd the entry that has
the correct arrival time:
To test your code so far, save and run your code, then type the following lines into
the shell and answer the questions:
city_id = get_city_id()
weather_data = get_weather_data(city_id)
arrival = get_arrival()
forecast = get_forecast(arrival, weather_data)
pprint(forecast)
You should see the forecast for the correct city and the date/time displayed on your
screen.
Even with pretty print the dictionary looks pretty messy. You could use the data
structure as it is, but it would be fairly easy to make mistakes and introduce errors
into your code. You’re better o� trying to create a new data structure to hold just the
weather data you need.
De�ne a new function that takes forecast as an argument and create an empty
dictionary to hold the new data:
14 of 19 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
def get_readable_forecast(forecast):
weather = {}
weather['cloudiness'] = forecast['clouds']['all']
weather['temperature'] = float(forecast['main']['temp'])
The humidity is the same, but needs to be type cast to an integer as it is always a
whole number:
weather['humidity'] = int(forecast['main']['humidity'])
Next is the rain. This one is a little awkward; if there’s no rain that day, the
dictionary will be empty, which will cause you problems. Using conditional
selection, you can check if the dictionary contains the key '3h' . If it does, you
can use the data. If not, you can set the rain to 0 .
if '3h' in forecast['rain']:
weather['rain'] = float(forecast['rain']['3h'])
else:
weather['rain'] = 0.0
weather['description'] = forecast['weather'][0]['description']
weather['wind'] = float(forecast['wind']['speed'])
Then return the newly created weather dictionary. The whole function should
look like this:
def get_readable_forecast(forecast):
weather = {}
weather['cloudiness'] = forecast['clouds']['all']
15 of 19 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
weather['temperature'] = float(forecast['main']['temp'])
weather['humidity'] = int(forecast['main']['humidity'])
if '3h' in forecast['rain']:
weather['rain'] = float(forecast['rain']['3h'])
else:
weather['rain'] = 0.0
weather['description'] = forecast['weather'][0]['description']
weather['wind'] = float(forecast['wind']['speed'])
return weather
Save and run the code again, then type the following into the shell:
city_id = get_city_id()
weather_data = get_weather_data(city_id)
arrival = get_arrival()
forecast = get_forecast(arrival, weather_data)
weather = get_readable_forecast(forecast)
pprint(weather)
{'cloudiness': 36,
'description': 'scattered clouds',
'humidity': 97,
'rain': 0.0,
'temperature': 283.99,
'wind': 2.76}
16 of 19 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
To �nish o�, your program will advise the user on what to wear. The function is going
to contain a lot of conditional selection, but shouldn’t need too much explaining. It’s
worth noting that the rain values have been divided by 3 to get the hourly rainfall.
If you want to change the values to suit your own particular feelings about what to
wear in di�erent conditions, then feel free - you can be as creative as you want.
def get_clothes(weather):
print('The overall description for the weather at that time is
{}'.format(weather['description']))
if weather['cloudiness'] < 10:
print('It should be sunny, so a hat or sunglasses might be
needed')
if weather['rain'] == 0:
print("It's not going to rain, so no umbrella is needed")
elif weather['rain']/3 < 2.5:
print("There'll be light rain, so consider a hood or umbrella")
elif weather['rain']/3 < 7.6:
print("There'll be moderate rain, so an umbrella is probably
needed")
elif weather['rain']/3 < 50:
print("There'll be heavy rain, so you'll need an umbrella and a
waterproof top")
elif weather['rain']/3 > 50:
print("There'll be violent rain, so wear a life-jacket")
if weather['temperature'] < 273:
print("It's going to be freezing, so take a heavy coat")
elif weather['temperature'] < 283:
print("It's going to be cold, so a coat or thick jumper might be
sensible")
elif weather['temperature'] < 293:
print("It's not too cold, but you might consider taking a light
jumper")
elif weather['temperature'] < 303:
print("Shorts and T-shirt weather :)")
if weather['wind'] > 30:
print("There'll be wind, so a jacket might be useful")
elif weather['wind'] > 10:
print("There'll be a light breeze, so maybe long sleeves might
be useful")
else:
17 of 19 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
Step 18 Finishing o�
The very last function will tie all the others together, and can be called at the bottom
of your script:
def main():
city_id = get_city_id()
weather_data = get_weather_data(city_id)
arrival = get_arrival()
forecast = get_forecast(arrival, weather_data)
weather = get_readable_forecast(forecast)
get_clothes(weather)
main()
None of the data inputs have been validated. This means a user could easily type
in a city name and forget the capital letter at the start, or type YES instead of y
when checking if the city is correct. They might even enter numbers outside of
the range of acceptable dates! You can guarantee that if a user can break your
software, then they will. Why not alter your code to make it more robust?
Why not integrate your program with a little Minecraft code? You could then add
graphical elements to the program to display the di�erent weathers in Minecraft.
18 of 19 11/25/18, 9:23 PM
Dress for the Weather | Raspberry Pi Projects https://projects.raspberrypi.org/en/projects/dress-...
19 of 19 11/25/18, 9:23 PM