Search This Blog

Thursday, November 24, 2022

Python Weather app - current conditions from the national weather service

 So, I was reading through a course on XML from W3 schools and in there, it mentioned that current conditions are available as an XML file for various locations.  So I investigated it.  After a bit, I thought I should make a Python program to pull the information from the National Weather Service in XML format and then display this information to the user.

It took some time to refamiliarize myself on the use of the urllib functions in Python to make an http request and download the text of the response.  Then, I needed to learn how to use the XML functions to parse the XML to retrieve the values for each of the tags.  My solution is quite fragile in that any changes to the file format by the National Weather Service will break the code.  Nonetheless, I was quite pleased with myself when I had finally got this working!  So I thought I would share it with the world.

While I don't think that this is going to revolutionize anything, maybe you can re-use some of the code.  Probably the list of the different Oregon locations is the most useful thing.

Here's what the user sees after hitting Fetch:


Here's the code:

import urllib.request
import xml.etree.ElementTree as ET
from tkinter import *

def getURLList():
    x = dict()
    x['Astoria']='https://w1.weather.gov/xml/current_obs/KAST.rss'
    x['Aurora']='https://w1.weather.gov/xml/current_obs/KUAO.rss'
    x['Baker']='https://w1.weather.gov/xml/current_obs/KBKE.rss'
    x['Bend']='https://w1.weather.gov/xml/current_obs/KBDN.rss'
    x['Brookings']='https://w1.weather.gov/xml/current_obs/KBOK.rss'
    x['Burns']='https://w1.weather.gov/xml/current_obs/KBNO.rss'
    x['Corvallis']='https://w1.weather.gov/xml/current_obs/KCVO.rss'
    x['Pendleton']='https://w1.weather.gov/xml/current_obs/KPDT.rss'
    x['Eugene']='https://w1.weather.gov/xml/current_obs/KEUG.rss'
    x['Florence']='https://w1.weather.gov/xml/current_obs/K6S2.rss'
    x['Gold Beach']='https://w1.weather.gov/xml/current_obs/K4S1.rss'
    x['Grant County']='https://w1.weather.gov/xml/current_obs/KGCD.rss'
    x['Grants Pass']='https://w1.weather.gov/xml/current_obs/K3S8.rss'
    x['Hermiston']='https://w1.weather.gov/xml/current_obs/KHRI.rss'
    x['Hobby']='https://w1.weather.gov/xml/current_obs/K77S.rss'
    x['Joseph']='https://w1.weather.gov/xml/current_obs/KJSY.rss'
    x['Ken Jerstedt']='https://w1.weather.gov/xml/current_obs/K4S2.rss'
    x['Klamath Falls']='https://w1.weather.gov/xml/current_obs/KLMT.rss'
    x['La Grande']='https://w1.weather.gov/xml/current_obs/KLGD.rss'
    x['Lake County']='https://w1.weather.gov/xml/current_obs/KLKV.rss'
    x['Lexington']='https://w1.weather.gov/xml/current_obs/K9S9.rss'
    x['Madras']='https://w1.weather.gov/xml/current_obs/KS33.rss'
    x['McMinneville']='https://w1.weather.gov/xml/current_obs/KMMV.rss'
    x['Meacham']='https://w1.weather.gov/xml/current_obs/KMEH.rss'
    x['Newport']='https://w1.weather.gov/xml/current_obs/KONP.rss'
    x['Ontario']='https://w1.weather.gov/xml/current_obs/KONP.rss'
    x['PDX Airport']='https://w1.weather.gov/xml/current_obs/KPDX.rss'
    x['Troutdale']='https://w1.weather.gov/xml/current_obs/KTTD.rss'
    x['Hillsboro']='https://w1.weather.gov/xml/current_obs/KHIO.rss'
    x['Prineville']='https://w1.weather.gov/xml/current_obs/KS39.rss'
    x['Redmond']='https://w1.weather.gov/xml/current_obs/KRDM.rss'
    x['Rogue Valley']='https://w1.weather.gov/xml/current_obs/KMFR.rss'
    x['Rome']='https://w1.weather.gov/xml/current_obs/KREO.rss'
    x['Roseburg']='https://w1.weather.gov/xml/current_obs/KRBG.rss'
    x['Salem']='https://w1.weather.gov/xml/current_obs/KSLE.rss'
    x['Scappoose']='https://w1.weather.gov/xml/current_obs/KSPB.rss'
    x['Sexton']='https://w1.weather.gov/xml/current_obs/KSXT.rss'
    x['SW Oregon']='https://w1.weather.gov/xml/current_obs/KOTH.rss'
    x['Sun River']='https://w1.weather.gov/xml/current_obs/KS21.rss'
    x['Tillamook']='https://w1.weather.gov/xml/current_obs/KTMK.rss'
    return x
class App:
    """This class is derived, I think, from a TK class and is designed to
    work with the TK environment."""
    def __init__(self, master):
        """This class is the whole of the application """
        # Necessary Frames
        frame = Frame(master)
        control_frame = Frame(frame)
        control_frame.grid(row = 0, column = 0, sticky = N)
        canvas_frame = Frame(frame)
        canvas_frame.grid(row =0, column = 1)
        #Application variables
        self.url_list = getURLList()
        self.key_list = list(self.url_list.keys())
        self.tmpstr = "Nothing yet"
        self.select = StringVar(master)
        self.select.set(self.key_list[0])
        
        #Control FrameWidgets
        self.lab = Label(canvas_frame, text=self.tmpstr)
        self.lab.pack(side = TOP)
        self.b1 = Button(control_frame, text='Fetch')
        self.b1.config(command=self.fetch)
        self.b1.pack()
        self.b2 = OptionMenu(control_frame, self.select, *self.key_list)
        self.b2.pack()
        frame.pack()
        #Menu's
        menubar = Menu(root)
        menu_1 = Menu(menubar, tearoff=0)
        menu_1.add_command(label='Quit',command=root.destroy)
        menubar.add_cascade(label='File', menu=menu_1)
        master.config(menu=menubar)
        
    def fetch(self):
        """ This function now goes out and gets the web page, parses the returned XML data
        and updates the label with the value"""
        selection = self.select.get() #creating a temp variable for ease
        with urllib.request.urlopen(self.url_list[selection]) as response:
           html = response.read().decode('utf-8') #.decode converts the rec'd bytes to str
        tree = ET.fromstring(html)
        tempstr = tree[0][0].text+'\n'
        tempstr = tempstr + tree[0][9][0].text +'\n'
        temp1 = tree[0][9][2].text
        n1 = temp1.rfind('>')+1
        tempstr = tempstr + temp1[n1:]
        self.lab.configure(text=tempstr)

if __name__ == '__main__':
    root = Tk()
    root.wm_title('Current Weather')
    app = App(root)
    root.mainloop()




No comments:

Post a Comment