Ruby po kapkách (23.) – stahujeme data o počasí

3. září 2009

V minulém díle jsme ve velmi zhuštěné podobě poznali základní aspekty práce se síťovým spojením v Ruby. Pohybovali jsem se přitom na relativně nízké úrovni. V našem programu jsme používali přímo TCP sockety. Dnes uděláme krok směrem k vyšší úrovni abstrakce síťových knihoven a zkusíme si vytvořit základ užitečné aplikace pro stahování informací o počasí.

Čerpat je budeme ze zpráv METAR, které se vytvářejí v meteorologických stanicích umístěných nejčastěji na letištích. Obvykle se vydávají jednou za hodinu, na frekventovaných letištích dvakrát za hodinu (http://cs.wikipedia.org/wiki/METAR). Zprávy jsou úsporně kódovány. Výše uvedená stránka na wikipedii obsahuje ukázku zprávy a popis, co která pozice znamená. Na internetu lze také nalézt zdrojové kódy algoritmu pro zpracování těchto meteorologických zpráv. My si situaci zjednodušíme a využijeme toho, že lze stáhnout i částečně dekódované zprávy, které již jsou čitelné i pro neškoleného člověka.

Tyto zprávy nalezneme na serveru tgftp.nws.noaa.gov, kde k nim lze přistupovat prostřednictvím protokolu FTP (File Transmission Protocol). FTP klient ovladatelný z příkazové řádky je součástí většiny operačních systémů. Pomocí něj si můžeme stáhnout textový soubor s aktuální dekódovanou zprávou pro pražské letiště, které má kód LKPR.

ftp> open tgftp.nws.noaa.gov
Connected to tgftp.nws.noaa.gov.
220 Authorized users only. All activity may be monitored and reported.
Name (tgftp.nws.noaa.gov:dali): anonymous
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> cd data/observations/metar/decoded
250 Directory successfully changed.
ftp> get LKPR.TXT
local: LKPR.TXT remote: LKPR.TXT
229 Entering Extended Passive Mode (|||38745|)
150 Opening BINARY mode data connection for LKPR.TXT (408 bytes).
100% |*************************************| 408 4.98 MB/s 00:00 ETA
226 File send OK.
408 bytes received in 00:00 (2.77 MB/s)

Stažený soubor pak vypadá takto:

dali@epot:~$ cat LKPR.TXT
Praha / Ruzyne, Czech Republic (LKPR) 50-06N 014-15E 365M
Aug 27, 2009 – 07:30 AM EDT / 2009.08.27 1130 UTC
Wind: from the WSW (240 degrees) at 6 MPH (5 KT):0
Visibility: greater than 7 mile(s):0
Sky conditions: mostly cloudy
Temperature: 80 F (27 C)
Dew Point: 59 F (15 C)
Relative Humidity: 47%
Pressure (altimeter): 30.09 in. Hg (1019 hPa)
ob: LKPR 271130Z 24005KT 9999 BKN045 27/15 Q1019 NOSIG
cycle: 11

Jak je vidět, dékodovaná zpráva je skutečně obyčejný textový soubor. První dva řádky obsahují informaci o lokalitě a času měření. Pak následují řádky s názvem veličiny a hodnotou navzájem oddělenými dvojtečkami. Jak bychom tedy mohli funkcionalitu zjišťování aktuální informace o počasí začlenit do naší aplikace v Ruby? Začněme stažením souboru. Ve standardní knihovně jsou třídy, které podporují aplikační protokoly jako HTTP nebo FTP. Kus kódu, který zajistí připojení k FTP serveru a uložení obsahu METAR zprávy do pole řádků může vypadat například takto.

require ‚net/ftp‘
 
metar = Array.new
Net::FTP.open(‚tgftp.nws.noaa.gov‘) do |ftp|
ftp.login
ftp.passive = true
ftp.chdir(‚data/observations/metar/decoded‘)
ftp.retrlines(‚RETR LKPR.TXT‘) { |line| metar << line }
end
 
3.times { puts metar.shift }

Využíváme třídu Net::FTP (Net je modul, který v tomto případě funguje jako jmenný prostor) ze standardní knihovny. Příslušnou část knihovny je třeba na začátku načíst pomocí metody require. Voláním metody třídy open vytvoříme instanci, kterou vzápětí předáme do bloku. Jedná se o stejný přístup, který jsme již viděli v případě souborů. Pokud by pokus o navázání spojení se serverem selhal, byl by blok interpretem přeskočen. Dále voláme metody instance. Metoda login obvykle akceptuje jako parametry uživatelské jméno a heslo pro přihlášení k FTP serveru. Server tgftp.nws.noaa.gov lze ale navštívit anonymně (uživatelské jméno anonymous a jako heslo řetězec odpovídající formátu e-mailové adresy), což za nás knihovna zajistí sama. Žádné parametry tedy nezadáváme. Voláním další metody (povšimněte si, že se jedná o setter) nastavíme relaci do tzv. pasivního módu. Protokol FTP v aktivním módu potřebuje otevření zpětného spojení ve směru od serveru ke klientovi, což nemusí vždy fungovat díky zabezpečení klientské sítě nebo počítače. Pasivní mód zpětné spojení nevyužívá.

Metoda chdir mění aktuální adresář na serveru. Poté voláme metodu retrlines, která načítá z FTP serveru soubor uvedený jako parametr a předává ho po řádcích do bloku. Pro ilustraci končí fragment kódu výpisem prvních tří prvků načteného pole.

c:\Tmp>ruby metar.rb
Praha / Ruzyne, Czech Republic (LKPR) 50-06N 014-15E 365M
Aug 29, 2009 – 07:30 AM EDT / 2009.08.29 1130 UTC
Wind: from the W (260 degrees) at 14 MPH (12 KT):0

Řekněme, že bychom do naší aplikace chtěli načíst čas měření a zjištěnou teplotu. Časový řetězec vypadá na první pohled dosti složitě. Ruby je naštěstí ve zpracování řetězců přeborníkem. Nahrahraďme poslední řádek kódu například následující konstrukcí.

if metar[1] =~ /(\d{4})\.(\d{2})\.(\d{2}) (\d{2})(\d{2}) UTC/
time = Time.utc($1, $2, $3, $4, $5).localtime
else
raise ‚Neplatná data!‘
end
 
puts metar[1] puts time

Využíváme regulární výraz, který odpovídá jednotlivým číslicím v časovém řetězci METAR zprávy. Ze získaných číslic vytvoříme instanci třídy Time standardní metodou. Čas zprávy je v pásmu UTC, takže si ho rovnou převedeme na lokální hodnotu. Pokud by regulární výraz neodpovídal, můžeme rozumně předpokládat, že došlo k chybě například při načítání souboru. Pokud odpovídá, vypadá výstup třeba takto.

c:\Tmp>ruby metar.rb
Aug 29, 2009 – 07:30 AM EDT / 2009.08.29 1130 UTC
Sat Aug 29 13:30:00 +0200 2009

Stejně jednoduše využijeme regulární výrazy i pro získání teploty ve stupních Celsia. Zde je kompletní výpis prográmku, který vypíše teplotu na Ruzyňském letišti a čas, kdy byla měřena. (Jako obvykle je však třeba upozornit, že je vynechána důslednější kontrola chyb.)

require ‚net/ftp‘
 
metar = Array.new
Net::FTP.open(‚tgftp.nws.noaa.gov‘) do |ftp|
ftp.login
ftp.passive = true
ftp.chdir(‚data/observations/metar/decoded‘)
ftp.retrlines(‚RETR LKPR.TXT‘) { |line| metar << line }
end
 
if metar[1] =~ /(\d{4})\.(\d{2})\.(\d{2}) (\d{2})(\d{2}) UTC/
time = Time.utc($1, $2, $3, $4, $5).localtime
else
raise ‚Neplatná data!‘
end
 
if metar.grep(/ˆTemperature/).first =~ /(-?[0-9]+) C/
temp = $1
else
raise ‚Neznámá teplota!‘
end
 
puts „Teplota #{time.strftime(‚%d.%m.%y‘)} ve #{time.strftime(‚%H:%M‘)} byla #{temp} stupňů Celsia.“

Doplněná poslední sekce prohledává pole řádků s údaji pomocí metody grep. Ta vrací pole. Nás však zajímá pouze první (a jediný) prvek, který opět porovnáváme s regulárním výrazem. Pokud výraz odpovídá, načteme si zjištěnou teplotu do proměnné. (Na tomto místě bychom možná ještě mohli použít metodu to_f pro převod řetězce na reálné číslo.) Spuštění přinese očekávaný výsledek.

c:\Tmp>ruby metar.rb
Teplota 29.08.09 ve 14:00 byla 20 stupňů Celsia.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *