Weather Forecast

November 5, 2010

The purpose of this exercise is two-fold: First, to get you into a corner of your language which might not be familiar to you. Second, to make you think about the vast amount of data that is available. If you’re not interested in American weather forecasts, pick a different data set that does interest you.

Many languages provide a built-in mechanism for retrieving files from the internet, but Scheme does not. We will use wget to connect to the internet and retrieve files, relying on the underlying Scheme interpreter to provide the non-standard (but widely available) functions file-exists?, delete-file, and system. First we write a function to find an unused filename to hold the file downloaded from the internet; it just loops until it finds a suitable candidate:

(define (tempname)
  (let loop ((i 0))
    (let ((f (string-append "temp" (number->string i))))
      (if (file-exists? f) (loop (+ i 1)) f))))

With-input-from-url uses wget to retrieve the file from the internet and save it in a temporary file, and with-input-from-file to make its contents available to Scheme:

(define (with-input-from-url url thunk)
  (let ((f (tempname)))
    (if (zero? (system (string-append wget " " f " " url)))
        (begin (with-input-from-file f thunk) (delete-file f #t))
        (error 'with-input-from-url "system error in wget"))))

How you run wget depends on your system. Here are three definitions that work on the systems available to me; yours may differ:

(define wget "c:\cygwin\bin\wget -qO") ; windows/cygwin
(define wget "/usr/local/bin/wget -qO") ; unix/hp
(define wget "/usr/bin/wget -q0") ; linux/ubuntu

The list of cities and states for which NOAA provides forecasts is available at http://weather.noaa.gov/pub/data/forecasts/city/. Here we provide a function that builds an appropriate url for the desired city and state:

(define (weather-url state city)
  (string-append
    "http://weather.noaa.gov/pub/data/forecasts/city/"
    state "/" city ".txt"))

All that’s left is to retrieve the url and output the file:

(define (display-weather state city)
  (with-input-from-url (weather-url state city)
    (lambda ()
      (do ((c (read-char) (read-char)))
          ((eof-object? c))
        (display c)))))

It’s a brisk autumn where I live:

> (display-weather "mo" "st_louis")
FPUS43 KLSX 030842

City Forecast for St Louis, MO
Issued Wednesday morning - Nov 3, 2010

.Wednesday... Sunny, high 64, 10% chance of precipitation.
.Wednesday night... Low 41, 5% chance of precipitation.
.Thursday... Partly cloudy, high 54, 10% chance of precipitation.
.Thursday night... Low 33.
.Friday... High 48.

You can see the code at http://programmingpraxis.codepad.org/0ppN34nS.

About these ads

Pages: 1 2

9 Responses to “Weather Forecast”

  1. My Haskell solution (see http://bonsaicode.wordpress.com/2010/11/05/programming-praxis-weather-forecast/ for a version with comments):

    import Network.HTTP
    
    showWeather :: String -> String -> IO ()
    showWeather state city = either print (putStrLn . rspBody) =<<
        simpleHTTP (getRequest $ "http://weather.noaa.gov/pub/data/\
            \forecasts/city/" ++ state ++ "/" ++ city ++ ".txt")
    
  2. novatech said

    function weather() { curl http://weather.noaa.gov/pub/data/forecasts/city/$1/$2.txt; }
    weather $1 $2

  3. Graham said

    This definitely made me think. At first I was tempted to just open the webpage itself.
    This is basically a less pretty knock-off of Remco’s Haskell.

    import sys
    import urllib
    
    def weather(s, c):
        a = "http://weather.noaa.gov/pub/data/forecasts/city/%s/%s.txt" % (s, c)
        u = urllib.urlopen(a)
        for l in u: print l
        u.close()
    
    if __name__ == '__main__':
        weather(sys.argv[1], sys.argv[2])
    
  4. slabounty said

    A ruby version

    require 'net/http'
    
    def display_weather(city, state)
        Net::HTTP.get_print URI.parse("http://weather.noaa.gov/pub/data/forecasts/city/#{state}/#{city}.txt")
    end
    
    display_weather('bakersfield', 'ca')
    
  5. Ian said

    Actually, `file-exists?` and `delete-file` are standard if you include R6RS, and it is my understanding that they will be included in the R7RS too.

    Anyway, I’m lazy so I used an existing http library for R6RS

    #!/usr/bin/env scheme-script
    #!r6rs
    (import (rnrs)
            (ocelotl net http)
            (ocelotl net http-client)
            (srfi :48 intermediate-format-strings))
    
    (define (display-weather state city)
      (define weather-url
        "http://weather.noaa.gov/pub/data/forecasts/city/~a/~a.txt")
      (let-values (((response body) (http-get (format #f weather-url state city)
                                              '((Accept-Charset . "utf-8")))))
        (if (= (http-response/status-code response) 200)
            (display (utf8->string body))
            (display "I'm sorry, there is no weather forecast for this city\n"))))
    
    (let ((args (command-line)))
      (if (= (length args) 3)
          (display-weather (cadr args) (caddr args))
          (display "Usage: forecast.ss STATE CITY\n")))
    
  6. Paul said

    A couple of lines for powershell

  7. Sorry for previous comment! it is on wrong post!
    That is for “subset sums “!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 629 other followers

%d bloggers like this: