pymetar-0.19/0000755000175000017530000000000011735301222014573 5ustar klausmanklausman00000000000000pymetar-0.19/bin/0000755000175000017530000000000011735301222015343 5ustar klausmanklausman00000000000000pymetar-0.19/bin/pymetar0000755000175000017530000000370011516603574016766 0ustar klausmanklausman00000000000000#!/usr/bin/python -tt # -*- coding: iso-8859-15 -*- __version__="1.1" import pymetar import sys if len(sys.argv)<2 or sys.argv[1]=="--help": sys.stderr.write("Usage: %s \n"% sys.argv[0]) sys.stderr.write("Station IDs can be found at: http://www.nws.noaa.gov/tg/siteloc.shtml\n") sys.exit(1) elif (sys.argv[1] == "--version"): print "%s v%s using pymetar lib v%s"%(sys.argv[0], __version__,pymetar.__version__) sys.exit(0) else: station=sys.argv[1] try: rf=pymetar.ReportFetcher(station) rep=rf.FetchReport() except Exception, e: sys.stderr.write("Something went wrong when fetching the report.\n") sys.stderr.write("These usually are transient problems if the station ") sys.stderr.write("ID is valid. \nThe error encountered was:\n") sys.stderr.write(str(e)+"\n") sys.exit(1) rp=pymetar.ReportParser() pr=rp.ParseReport(rep) print "Weather report for %s (%s) as of %s" %\ (pr.getStationName(), station, pr.getISOTime()) print "Values of \"None\" indicate that the value is missing from the report." print "Temperature: %s C / %s F" %\ (pr.getTemperatureCelsius(), pr.getTemperatureFahrenheit()) if pr.getWindchill() and pr.getWindchillF(): print "Wind chill: %.2f C / %.2f F" % (pr.getWindchill(), pr.getWindchillF()) print "Rel. Humidity: %s%%" % (pr.getHumidity()) if pr.getWindSpeed() is not None: print "Wind speed: %0.2f m/s (%i Bft, %0.2f knots)" % \ (pr.getWindSpeed(), pr.getWindSpeedBeaufort(), pr.getWindSpeedKnots()) else: print "Wind speed: None" print "Wind direction: %s deg (%s)" %\ (pr.getWindDirection(), pr.getWindCompass()) if pr.getPressure() is not None: print "Pressure: %s hPa" % (int(pr.getPressure())) else: print "Pressure: None" print "Dew Point: %s C / %s F" %\ (pr.getDewPointCelsius(), pr.getDewPointFahrenheit()) print "Weather:",pr.getWeather() print "Cloudtype:",pr.getCloudtype() print "Sky Conditions:",pr.getSkyConditions() pymetar-0.19/setup.py0000755000175000017530000000202011710005457016306 0ustar klausmanklausman00000000000000#! /usr/bin/env python # # pymetar # (c) 2002 Tobias Klausmann # (c) 2002 Jerome Alet # You're welcome to redistribute this software under the # terms of the GNU General Public Licence version 2.0 # or, at your option, any higher version. # # You can read the complete GNU GPL in the file COPYING # which should come along with this software, or visit # the Free Software Foundation's WEB site http://www.fsf.org # # $Id: $ from distutils.core import setup import pymetar setup(name = "pymetar", version = pymetar.__version__, license = "GNU GPL", description = pymetar.__doc__, author = "Tobias Klausmann", author_email = "klausman-pymetar@schwarzvogel.de", url = "http://www.schwarzvogel.de/software-pymetar.shtml", packages= [ "" ], py_modules=["pymetar"], scripts = [ "bin/pymetar" ], data_files = [("share/doc/pymetar-%s"%pymetar.__version__, ['README', 'COPYING', 'THANKS', 'librarydoc.txt']), ("share/man/man1", ['pymetar.1'])] ) pymetar-0.19/pymetar.py0000644000175000017530000012416611734155156016654 0ustar klausmanklausman00000000000000# -*- coding: utf8 -*- # pylint: disable-msg=C0103 # Disable naming style messages """Pymetar (c) 2002-2012 Tobias Klausmann Pymetar is a python module and command line tool designed to fetch Metar reports from the NOAA (http://www.noaa.gov) and allow access to the included weather information. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.""" import fpformat import math import re import urllib2 __author__ = "klausman-pymetar@schwarzvogel.de" __version__ = "0.19" __revision__ = "$Rev$"[6:-2] CLOUD_RE_STR = (r"^(CAVOK|CLR|SKC|BKN|SCT|FEW|OVC|NSC)([0-9]{3})?" r"(TCU|CU|CB|SC|CBMAM|ACC|SCSL|CCSL|ACSL)?$") COND_RE_STR = (r"^(-|\\+)?(VC|MI|BC|PR|TS|BL|SH|DR|FZ)?(DZ|RA|SN|SG|IC|PE|" r"GR|GS|UP|BR|FG|FU|VA|SA|HZ|PY|DU|SQ|SS|DS|PO|\\+?FC)$") class EmptyReportException(Exception): """This gets thrown when the ReportParser gets fed an empty report""" pass class EmptyIDException(Exception): """This gets thrown when the ReportFetcher is called with an empty ID""" pass class NetworkException(Exception): """This gets thrown when a network error occurs""" pass # What a boring list to type ! # # It seems the NOAA doesn't want to return plain text, but considering the # format of their response, this is not to save bandwidth :-) _WEATHER_CONDITIONS = { "DZ" : ("Drizzle", "rain", { "" : "Moderate drizzle", "-" : "Light drizzle", "+" : "Heavy drizzle", "VC" : "Drizzle in the vicinity", "MI" : "Shallow drizzle", "BC" : "Patches of drizzle", "PR" : "Partial drizzle", "TS" : ("Thunderstorm", "storm"), "BL" : "Windy drizzle", "SH" : "Showers", "DR" : "Drifting drizzle", "FZ" : "Freezing drizzle", }), "RA" : ("Rain", "rain", { "" : "Moderate rain", "-" : "Light rain", "+" : "Heavy rain", "VC" : "Rain in the vicinity", "MI" : "Shallow rain", "BC" : "Patches of rain", "PR" : "Partial rainfall", "TS" : ("Thunderstorm", "storm"), "BL" : "Blowing rainfall", "SH" : "Rain showers", "DR" : "Drifting rain", "FZ" : "Freezing rain", }), "SN" : ("Snow", "snow", { "" : "Moderate snow", "-" : "Light snow", "+" : "Heavy snow", "VC" : "Snow in the vicinity", "MI" : "Shallow snow", "BC" : "Patches of snow", "PR" : "Partial snowfall", "TS" : ("Snowstorm", "storm"), "BL" : "Blowing snowfall", "SH" : "Snowfall showers", "DR" : "Drifting snow", "FZ" : "Freezing snow", }), "SG" : ("Snow grains", "snow", { "" : "Moderate snow grains", "-" : "Light snow grains", "+" : "Heavy snow grains", "VC" : "Snow grains in the vicinity", "MI" : "Shallow snow grains", "BC" : "Patches of snow grains", "PR" : "Partial snow grains", "TS" : ("Snowstorm", "storm"), "BL" : "Blowing snow grains", "SH" : "Snow grain showers", "DR" : "Drifting snow grains", "FZ" : "Freezing snow grains", }), "IC" : ("Ice crystals", "snow", { "" : "Moderate ice crystals", "-" : "Few ice crystals", "+" : "Heavy ice crystals", "VC" : "Ice crystals in the vicinity", "BC" : "Patches of ice crystals", "PR" : "Partial ice crystals", "TS" : ("Ice crystal storm", "storm"), "BL" : "Blowing ice crystals", "SH" : "Showers of ice crystals", "DR" : "Drifting ice crystals", "FZ" : "Freezing ice crystals", }), "PE" : ("Ice pellets", "snow", { "" : "Moderate ice pellets", "-" : "Few ice pellets", "+" : "Heavy ice pellets", "VC" : "Ice pellets in the vicinity", "MI" : "Shallow ice pellets", "BC" : "Patches of ice pellets", "PR" : "Partial ice pellets", "TS" : ("Ice pellets storm", "storm"), "BL" : "Blowing ice pellets", "SH" : "Showers of ice pellets", "DR" : "Drifting ice pellets", "FZ" : "Freezing ice pellets", }), "GR" : ("Hail", "rain", { "" : "Moderate hail", "-" : "Light hail", "+" : "Heavy hail", "VC" : "Hail in the vicinity", "MI" : "Shallow hail", "BC" : "Patches of hail", "PR" : "Partial hail", "TS" : ("Hailstorm", "storm"), "BL" : "Blowing hail", "SH" : "Hail showers", "DR" : "Drifting hail", "FZ" : "Freezing hail", }), "GS" : ("Small hail", "rain", { "" : "Moderate small hail", "-" : "Light small hail", "+" : "Heavy small hail", "VC" : "Small hail in the vicinity", "MI" : "Shallow small hail", "BC" : "Patches of small hail", "PR" : "Partial small hail", "TS" : ("Small hailstorm", "storm"), "BL" : "Blowing small hail", "SH" : "Showers of small hail", "DR" : "Drifting small hail", "FZ" : "Freezing small hail", }), "UP" : ("Precipitation", "rain", { "" : "Moderate precipitation", "-" : "Light precipitation", "+" : "Heavy precipitation", "VC" : "Precipitation in the vicinity", "MI" : "Shallow precipitation", "BC" : "Patches of precipitation", "PR" : "Partial precipitation", "TS" : ("Unknown thunderstorm", "storm"), "BL" : "Blowing precipitation", "SH" : "Showers, type unknown", "DR" : "Drifting precipitation", "FZ" : "Freezing precipitation", }), "BR" : ("Mist", "fog", { "" : "Moderate mist", "-" : "Light mist", "+" : "Thick mist", "VC" : "Mist in the vicinity", "MI" : "Shallow mist", "BC" : "Patches of mist", "PR" : "Partial mist", "BL" : "Mist with wind", "DR" : "Drifting mist", "FZ" : "Freezing mist", }), "FG" : ("Fog", "fog", { "" : "Moderate fog", "-" : "Light fog", "+" : "Thick fog", "VC" : "Fog in the vicinity", "MI" : "Shallow fog", "BC" : "Patches of fog", "PR" : "Partial fog", "BL" : "Fog with wind", "DR" : "Drifting fog", "FZ" : "Freezing fog", }), "FU" : ("Smoke", "fog", { "" : "Moderate smoke", "-" : "Thin smoke", "+" : "Thick smoke", "VC" : "Smoke in the vicinity", "MI" : "Shallow smoke", "BC" : "Patches of smoke", "PR" : "Partial smoke", "TS" : ("Smoke w/ thunders", "storm"), "BL" : "Smoke with wind", "DR" : "Drifting smoke", }), "VA" : ("Volcanic ash", "fog", { "" : "Moderate volcanic ash", "+" : "Thick volcanic ash", "VC" : "Volcanic ash in the vicinity", "MI" : "Shallow volcanic ash", "BC" : "Patches of volcanic ash", "PR" : "Partial volcanic ash", "TS" : ("Volcanic ash w/ thunders", "storm"), "BL" : "Blowing volcanic ash", "SH" : "Showers of volcanic ash", "DR" : "Drifting volcanic ash", "FZ" : "Freezing volcanic ash", }), "SA" : ("Sand", "fog", { "" : "Moderate sand", "-" : "Light sand", "+" : "Heavy sand", "VC" : "Sand in the vicinity", "BC" : "Patches of sand", "PR" : "Partial sand", "BL" : "Blowing sand", "DR" : "Drifting sand", }), "HZ" : ("Haze", "fog", { "" : "Moderate haze", "-" : "Light haze", "+" : "Thick haze", "VC" : "Haze in the vicinity", "MI" : "Shallow haze", "BC" : "Patches of haze", "PR" : "Partial haze", "BL" : "Haze with wind", "DR" : "Drifting haze", "FZ" : "Freezing haze", }), "PY" : ("Sprays", "fog", { "" : "Moderate sprays", "-" : "Light sprays", "+" : "Heavy sprays", "VC" : "Sprays in the vicinity", "MI" : "Shallow sprays", "BC" : "Patches of sprays", "PR" : "Partial sprays", "BL" : "Blowing sprays", "DR" : "Drifting sprays", "FZ" : "Freezing sprays", }), "DU" : ("Dust", "fog", { "" : "Moderate dust", "-" : "Light dust", "+" : "Heavy dust", "VC" : "Dust in the vicinity", "BC" : "Patches of dust", "PR" : "Partial dust", "BL" : "Blowing dust", "DR" : "Drifting dust", }), "SQ" : ("Squall", "storm", { "" : "Moderate squall", "-" : "Light squall", "+" : "Heavy squall", "VC" : "Squall in the vicinity", "PR" : "Partial squall", "TS" : "Thunderous squall", "BL" : "Blowing squall", "DR" : "Drifting squall", "FZ" : "Freezing squall", }), "SS" : ("Sandstorm", "fog", { "" : "Moderate sandstorm", "-" : "Light sandstorm", "+" : "Heavy sandstorm", "VC" : "Sandstorm in the vicinity", "MI" : "Shallow sandstorm", "PR" : "Partial sandstorm", "TS" : ("Thunderous sandstorm", "storm"), "BL" : "Blowing sandstorm", "DR" : "Drifting sandstorm", "FZ" : "Freezing sandstorm", }), "DS" : ("Duststorm", "fog", { "" : "Moderate duststorm", "-" : "Light duststorm", "+" : "Heavy duststorm", "VC" : "Duststorm in the vicinity", "MI" : "Shallow duststorm", "PR" : "Partial duststorm", "TS" : ("Thunderous duststorm", "storm"), "BL" : "Blowing duststorm", "DR" : "Drifting duststorm", "FZ" : "Freezing duststorm", }), "PO" : ("Dustwhirls", "fog", { "" : "Moderate dustwhirls", "-" : "Light dustwhirls", "+" : "Heavy dustwhirls", "VC" : "Dustwhirls in the vicinity", "MI" : "Shallow dustwhirls", "BC" : "Patches of dustwhirls", "PR" : "Partial dustwhirls", "BL" : "Blowing dustwhirls", "DR" : "Drifting dustwhirls", }), "+FC" : ("Tornado", "storm", { "" : "Moderate tornado", "+" : "Raging tornado", "VC" : "Tornado in the vicinity", "PR" : "Partial tornado", "TS" : "Thunderous tornado", "BL" : "Tornado", "DR" : "Drifting tornado", "FZ" : "Freezing tornado", }), "FC" : ("Funnel cloud", "fog", { "" : "Moderate funnel cloud", "-" : "Light funnel cloud", "+" : "Thick funnel cloud", "VC" : "Funnel cloud in the vicinity", "MI" : "Shallow funnel cloud", "BC" : "Patches of funnel cloud", "PR" : "Partial funnel cloud", "BL" : "Funnel cloud w/ wind", "DR" : "Drifting funnel cloud", }), } CLOUDTYPES = { "ACC": "altocumulus castellanus", "ACSL": "standing lenticular altocumulus", "CB": "cumulonimbus", "CBMAM": "cumulonimbus mammatus", "CCSL": "standing lenticular cirrocumulus", "CU": "cumulus", "SCSL": "standing lenticular stratocumulus", "SC": "stratocumulus", "TCU": "towering cumulus" } def metar_to_iso8601(metardate) : """Convert a metar date to an ISO8601 date.""" if metardate is not None: (date, hour) = metardate.split()[:2] (year, month, day) = date.split('.') # assuming tz is always 'UTC', aka 'Z' return ("%s-%s-%s %s:%s:00Z" % (year, month, day, hour[:2], hour[2:4])) def _parse_lat_long(latlong): """ Parse Lat or Long in METAR notation into float values. N and E are +, S and W are -. Expects one positional string and returns one float value. """ # I know, I could invert this if and put # the rest of the function into its block, # but I find it to be more readable this way if latlong is None: return None cap_inp = latlong.upper().strip() elements = cap_inp.split('-') # Extract N/S/E/W compass_dir = elements[-1][-1] # get rid of compass direction elements[-1] = elements[-1][:-1] elements = [int(i) for i in elements] coords = 0.0 elen = len(elements) if elen > 2: coords = coords + float(elements[2])/3600.0 if elen > 1: coords = coords + float(elements[1])/60.0 coords = coords + float(elements[0]) if compass_dir in ('W', 'S'): coords = -1.0*coords return coords class WeatherReport: """Incorporates both the unparsed textual representation of the weather report and the parsed values as soon as they are filled in by ReportParser.""" def _clearallfields(self): """Clear all fields values.""" # until finished, report is invalid self.valid = 0 # Clear all self.givenstationid = None self.fullreport = None self.temp = None self.tempf = None self.windspeed = None self.windspeedmph = None self.winddir = None self.vis = None self.dewp = None self.dewpf = None self.humid = None self.press = None self.pressmmHg = None self.code = None self.weather = None self.sky = None self.fulln = None self.cycle = None self.windcomp = None self.rtime = None self.pixmap = None self.latitude = None self.longitude = None self.altitude = None self.stat_city = None self.stat_country = None self.reporturl = None self.latf = None self.longf = None self.cloudinfo = None self.conditions = None self.w_chill = None self.w_chillf = None self.cloudtype = None def __init__(self, MetarStationCode = None): """Clear all fields and fill in wanted station id.""" self._clearallfields() self.givenstationid = MetarStationCode def getFullReport(self): """ Return the complete weather report. """ return self.fullreport def getTemperatureCelsius(self): """ Return the temperature in degrees Celsius. """ return self.temp def getTemperatureFahrenheit(self): """ Return the temperature in degrees Fahrenheit. """ return self.tempf def getDewPointCelsius(self): """ Return dewpoint in degrees Celsius. """ return self.dewp def getDewPointFahrenheit(self): """ Return dewpoint in degrees Fahrenheit. """ return self.dewpf def getWindSpeed(self): """ Return the wind speed in meters per second. """ return self.windspeed def getWindSpeedMilesPerHour(self): """ Return the wind speed in miles per hour. """ if self.windspeed is not None: return self.windspeedmph def getWindSpeedBeaufort(self): """ Return the wind speed in the Beaufort scale cf. http://en.wikipedia.org/wiki/Beaufort_scale """ if self.windspeed is not None: return round(math.pow(self.windspeed/0.8359648, 2/3.0)) def getWindSpeedKnots(self): """ Return the wind speed in knots """ if self.windspeed is not None: return self.windspeed * 1.94384449 def getWindDirection(self): """ Return wind direction in degrees. """ return self.winddir def getWindCompass(self): """ Return wind direction as compass direction (e.g. NE or SSE) """ return self.windcomp def getVisibilityKilometers(self): """ Return visibility in km. """ if self.vis is not None: return self.vis def getVisibilityMiles(self): """ Return visibility in miles. """ if self.vis is not None: return self.vis / 1.609344 def getHumidity(self): """ Return relative humidity in percent. """ return self.humid def getPressure(self): """ Return pressure in hPa. """ return self.press def getPressuremmHg(self): """ Return pressure in mmHg. """ return self.pressmmHg def getRawMetarCode(self): """ Return the encoded weather report. """ return self.code def getWeather(self): """ Return short weather conditions """ return self.weather def getSkyConditions(self): """ Return sky conditions """ return self.sky def getStationName(self): """ Return full station name """ return self.fulln def getStationCity(self): """ Return city-part of station name """ return self.stat_city def getStationCountry(self): """ Return country-part of station name """ return self.stat_country def getCycle(self): """ Return cycle value. The cycle value is not the frequency or delay between observations but the "time slot" in which the observation was made. There are 24 cycle slots every day which usually last from N:45 to N+1:45. The cycle from 23:45 to 0:45 is cycle 0. """ return self.cycle def getStationPosition(self): """ Return latitude, longitude and altitude above sea level of station as a tuple. Some stations don't deliver altitude, for those, None is returned as altitude. The lat/longs are expressed as follows: xx-yyd where xx is degrees, yy minutes and d the direction. Thus 51-14N means 51 degrees, 14 minutes north. d may take the values N, S for latitues and W, E for longitudes. Latitude and Longitude may include seconds. Altitude is always given as meters above sea level, including a trailing M. Schipohl Int. Airport Amsterdam has, for example: ('52-18N', '004-46E', '-2M') Moenchengladbach (where I live): ('51-14N', '063-03E', None) If you need lat and long as float values, look at getStationPositionFloat() instead """ # convert self.altitude to string for consistency return (self.latitude, self.longitude, "%s"%self.altitude) def getStationPositionFloat(self): """ Return latitude and longitude as float values in a tuple (lat, long, alt). """ return (self.latf, self.longf, self.altitude) def getStationLatitude(self) : """ Return the station's latitude in dd-mm[-ss]D format : dd : degrees mm : minutes ss : seconds D : direction (N, S, E, W) """ return self.latitude def getStationLatitudeFloat(self): """ Return latitude as a float """ return self.latf def getStationLongitude(self) : """ Return the station's longitude in dd-mm[-ss]D format : dd : degrees mm : minutes ss : seconds D : direction (N, S, E, W) """ return self.longitude def getStationLongitudeFloat(self): """ Return Longitude as a float """ return self.longf def getStationAltitude(self) : """ Return the station's altitude above the sea in meters. """ return self.altitude def getReportURL(self): """ Return the URL from which the report was fetched. """ return self.reporturl def getTime(self): """ Return the time when the observation was made. Note that this is *not* the time when the report was fetched by us Format: YYYY.MM.DD HHMM UTC Example: 2002.04.01 1020 UTC """ return self.rtime def getISOTime(self): """ Return the time when the observation was made in ISO 8601 format (e.g. 2002-07-25 15:12:00Z) """ return(metar_to_iso8601(self.rtime)) def getPixmap(self): """ Return a suggested pixmap name, without extension, depending on current weather. """ return self.pixmap def getCloudinfo(self): """ Return a tuple consisting of the parsed cloud information and a suggest pixmap name """ return self.cloudinfo def getConditions(self): """ Return a tuple consisting of the parsed sky conditions and a suggested pixmap name """ return self.conditions def getWindchill(self): """ Return wind chill in degrees Celsius """ # http://en.wikipedia.org/wiki/Wind_chill - North American wind chill # index if self.w_chill is None: if (self.temp and self.temp <= 10 and self.windspeed and (self.windspeed*3.6) > 4.8): self.w_chill = (13.12 + 0.6215*self.temp - 11.37*(self.windspeed*3.6)**0.16 + 0.3965*self.temp*(self.windspeed*3.6)**0.16) return self.w_chill def getWindchillF(self): """ Return wind chill in degrees Fahrenheit """ # http://en.wikipedia.org/wiki/Wind_chill - North American wind chill # index if self.w_chillf is None: if (self.tempf and self.tempf <= 50 and self.windspeedmph and self.windspeedmph >= 3): self.w_chillf = (35.74 + 0.6215*self.tempf - 35.75*self.windspeedmph**0.16 + 0.4275*self.tempf*self.windspeedmph**0.16) else: self.w_chillf = self.tempf return self.w_chillf def getCloudtype(self): """ Return cloud type information """ return self.cloudtype class ReportParser: """Parse raw METAR data from a WeatherReport object into actual values and return the object with the values filled in.""" def __init__(self, MetarReport = None): """Set attribute Report as specified on instantation.""" self.Report = MetarReport def extractCloudInformation(self) : """ Extract cloud information. Return None or a tuple (sky type as a string of text, cloud type (if any) and suggested pixmap name) """ matches = self.match_WeatherPart(CLOUD_RE_STR) skytype = None ctype = None pixmap = None for wcloud in matches: if wcloud is not None: stype = wcloud[:3] if stype in ("CLR", "SKC", "CAV", "NSC"): skytype = "Clear sky" pixmap = "sun" elif stype == "BKN" : skytype = "Broken clouds" pixmap = "suncloud" elif stype == "SCT" : skytype = "Scattered clouds" pixmap = "suncloud" elif stype == "FEW" : skytype = "Few clouds" pixmap = "suncloud" elif stype == "OVC" : skytype = "Overcast" pixmap = "cloud" if ctype == None: ctype = CLOUDTYPES.get(wcloud[6:], None) return (skytype, ctype, pixmap) def extractSkyConditions(self) : """ Extract sky condition information from the encoded report. Return a tuple containing the description of the sky conditions as a string and a suggested pixmap name for an icon representing said sky condition. """ matches = self.match_WeatherPart(COND_RE_STR) for wcond in matches: if ((len(wcond)>3) and (wcond.startswith('+') or wcond.startswith('-'))): wcond = wcond[1:] if wcond.startswith('+') or wcond.startswith('-'): pphen = 1 elif len(wcond) < 4: pphen = 0 else : pphen = 2 squal = wcond[:pphen] sphen = wcond[pphen : pphen + 4] phenomenon = _WEATHER_CONDITIONS.get(sphen, None) if phenomenon is not None : (name, pixmap, phenomenon) = phenomenon pheninfo = phenomenon.get(squal, name) if type(pheninfo) != type(()) : return (pheninfo, pixmap) else : # contains pixmap info return pheninfo def match_WeatherPart(self, regexp) : """ Return the matching part of the encoded Metar report. regexp: the regexp needed to extract this part. Return the first matching string or None. WARNING: Some Metar reports may contain several matching strings, only the first one is taken into account! """ matches = [] if self.Report.code is not None : myre = re.compile(regexp) for wpart in self.Report.getRawMetarCode().split() : match = myre.match(wpart) if match: matches.append(match.string[match.start(0) : match.end(0)]) return matches def ParseReport(self, MetarReport = None): """Take report with raw info only and return it with in parsed values filled in. Note: This function edits the WeatherReport object you supply!""" if self.Report is None and MetarReport is None: raise EmptyReportException( "No report given on init and ParseReport().") elif MetarReport is not None: self.Report = MetarReport lines = self.Report.fullreport.split("\n") for line in lines: try: header, data = line.split(":", 1) except ValueError: header = data = line header = header.strip() data = data.strip() # The station id inside the report # As the station line may contain additional sets of (), # we have to search from the rear end and flip things around if header.find("("+self.Report.givenstationid+")") != -1: id_offset = header.find("("+self.Report.givenstationid+")") loc = data[:id_offset] coords = data[id_offset:] try: loc = loc.strip() rloc = loc[::-1] rcoun, rcity = rloc.split(",", 1) except ValueError: rcity = "" rcoun = "" coords = data try: lat, lng, alt = coords.split()[1:4] alt = int(alt[:-1]) # cut off 'M' for meters except ValueError: (lat, lng) = coords.split()[1:3] alt = None # A few jokers out there think O==0 if "O" in lat: lat = lat.replace("O", "0") if "O" in lng: lng = lng.replace("O", "0") self.Report.stat_city = rcity.strip()[::-1] self.Report.stat_country = rcoun.strip()[::-1] self.Report.fulln = loc self.Report.latitude = lat self.Report.longitude = lng self.Report.latf = _parse_lat_long(lat) self.Report.longf = _parse_lat_long(lng) self.Report.altitude = alt # The line containing date and time of the report # We have to make sure that the station ID is *not* # in this line to avoid trying to parse the ob: line elif ((data.find(" UTC")) != -1 and (data.find(self.Report.givenstationid)) == -1): rtime = data.split("/")[1] self.Report.rtime = rtime.strip() # temperature elif (header == "Temperature"): fnht, cels = data.split(None, 3)[0:3:2] self.Report.tempf = float(fnht) # The string we have split is "(NN C)", hence the slice self.Report.temp = float(cels[1:]) # wind chill elif (header == "Windchill"): fnht, cels = data.split(None, 3)[0:3:2] self.Report.w_chillf = float(fnht) # The string we have split is "(NN C)", hence the slice self.Report.w_chill = float(cels[1:]) # wind dir and speed elif (header == "Wind"): if (data.find("Calm") != -1): self.Report.windspeed = 0.0 self.Report.windspeedkt = 0.0 self.Report.windspeedmph = 0.0 self.Report.winddir = None self.Report.windcomp = None elif (data.find("Variable") != -1): speed = data.split(" ", 3)[2] self.Report.windspeed = (float(speed)*0.44704) self.Report.windspeedkt = int(data.split(" ", 5)[4][1:]) self.Report.windspeedmph = int(speed) self.Report.winddir = None self.Report.windcomp = None else: fields = data.split(" ", 9)[0:9] comp = fields[2] deg = fields[3] speed = fields[6] speedkt = fields[8][1:] self.Report.winddir = int(deg[1:]) self.Report.windcomp = comp.strip() self.Report.windspeed = (float(speed)*0.44704) self.Report.windspeedkt = (int(speedkt)) self.Report.windspeedmph = int(speed) # visibility elif (header == "Visibility"): for visgroup in data.split(): try: self.Report.vis = float(visgroup)*1.609344 break except ValueError: self.Report.vis = None break # dew point elif (header == "Dew Point"): fnht, cels = data.split(None, 3)[0:3:2] self.Report.dewpf = float(fnht) # The string we have split is "(NN C)", hence the slice self.Report.dewp = float(cels[1:]) # humidity elif (header == "Relative Humidity"): h = data.split("%", 1)[0] self.Report.humid = int(h) # pressure elif (header == "Pressure (altimeter)"): press = data.split(" ", 1)[0] self.Report.press = (float(press)*33.863886) # 1 in = 25.4 mm => 1 inHg = 25.4 mmHg self.Report.pressmmHg = (float(press)*25.4000) # shot weather desc. ("rain", "mist", ...) elif (header == "Weather"): self.Report.weather = data # short desc. of sky conditions elif (header == "Sky conditions"): self.Report.sky = data # the encoded report itself elif (header == "ob"): self.Report.code = data.strip() # the cycle value ("time slot") elif (header == "cycle"): try: self.Report.cycle = int(data) except ValueError: # cycle value is missing or garbled, assume cycle 0 # TODO: parse the date/time header if it isn't too involved self.Report.cycle = 0 # cloud info cloudinfo = self.extractCloudInformation() (cloudinfo, cloudtype, cloudpixmap) = cloudinfo conditions = self.extractSkyConditions() if conditions is not None : (conditions, condpixmap) = conditions else : (conditions, condpixmap) = (None, None) # Some people might want to always use sky or cloud info specifially self.Report.cloudinfo = (cloudinfo, cloudpixmap) self.Report.conditions = (conditions, condpixmap) # fill the weather information self.Report.weather = self.Report.weather or conditions or cloudinfo # Pixmap guessed from general conditions has priority # over pixmap guessed from clouds self.Report.pixmap = condpixmap or cloudpixmap # Cloud type (Cumulonimbus etc.) if self.Report.cloudtype == None: self.Report.cloudtype = cloudtype # report is complete self.Report.valid = 1 return self.Report class ReportFetcher: """Fetches a report from a given METAR id, optionally taking into account a different baseurl and using environment var-specified proxies.""" def __init__(self, MetarStationCode = None, baseurl = "http://weather.noaa.gov/pub/data/observations/metar/decoded/"): """Set stationid attribute and base URL to fetch report from""" self.stationid = MetarStationCode self.baseurl = baseurl def MakeReport(self, StationID, RawReport): """ Take a string (RawReport) and a station code and turn it into an object suitable for ReportParser """ self.reporturl = "%s%s.TXT" % (self.baseurl, StationID) self.fullreport = RawReport report = WeatherReport(StationID) report.reporturl = self.reporturl report.fullreport = self.fullreport self.report = report # Caching it for GetReport() return report def FetchReport(self, StationCode = None, proxy = None): """ Fetch a report for a given station ID from the baseurl given upon creation of the ReportFetcher instance. If proxy is not None, a proxy URL of the form protocol://user:password@host.name.tld:port/ is expected, for example: http://squid.somenet.com:3128/ If no proxy is specified, the environment variable http_proxy is inspected. If it isn't set, a direct connection is tried. """ if self.stationid is None and StationCode is None: raise EmptyIDException( "No ID given on init and FetchReport().") elif StationCode is not None: self.stationid = StationCode self.stationid = self.stationid.upper() self.reporturl = "%s%s.TXT" % (self.baseurl, self.stationid) if proxy: p_dict = {'http': proxy} p_handler = urllib2.ProxyHandler(p_dict) opener = urllib2.build_opener(p_handler, urllib2.HTTPHandler) urllib2.install_opener(opener) else: urllib2.install_opener( urllib2.build_opener(urllib2.ProxyHandler, urllib2.HTTPHandler)) try: fn = urllib2.urlopen(self.reporturl) except urllib2.HTTPError, why: raise NetworkException, why # Dump entire report in a variable self.fullreport = fn.read() if fn.info().status: raise NetworkException, "Could not fetch METAR report" report = WeatherReport(self.stationid) report.reporturl = self.reporturl report.fullreport = self.fullreport self.report = report # Caching it for GetReport() return report def GetReport(self): """Get a previously fetched report again""" return self.report pymetar-0.19/librarydoc.txt0000644000175000017530000002444511710005577017506 0ustar klausmanklausman00000000000000Help on module pymetar: NAME pymetar - Pymetar v0.19 (c) 2002-2011 Tobias Klausmann FILE pymetar.py DESCRIPTION Pymetar is a python module and command line tool designed to fetch Metar reports from the NOAA (http://www.noaa.gov) and allow access to the included weather information. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. Please e-mail bugs to: klausman-pymetar@schwarzvogel.de CLASSES exceptions.Exception(exceptions.BaseException) EmptyIDException EmptyReportException NetworkException ReportFetcher ReportParser WeatherReport class EmptyIDException(exceptions.Exception) | This gets thrown when the ReportFetcher is called with an empty ID class EmptyReportException(exceptions.Exception) | This gets thrown when the ReportParser gets fed an empty report class NetworkException(exceptions.Exception) | This gets thrown when a network error occurs class ReportFetcher | Fetches a report from a given METAR id, optionally taking into | account a different baseurl and using environment var-specified | proxies. | | Methods defined here: | | FetchReport(self, StationCode=None, proxy=None) | Fetch a report for a given station ID from the baseurl given | upon creation of the ReportFetcher instance. | If proxy is not None, a proxy URL of the form | protocol://user:password@host.name.tld:port/ | is expected, for example: | http://squid.somenet.com:3128/ | If no proxy is specified, the environment variable http_proxy | is inspected. If it isn't set, a direct connection is tried. | | GetReport(self) | Get a previously fetched report again | | MakeReport(self, StationID, RawReport) | Take a string (RawReport) and a station code and turn it | into an object suitable for ReportParser | | __init__(self, MetarStationCode=None, baseurl='http://weather.noaa.gov/pub/data/observations/metar/decoded/') | Set stationid attribute and base URL to fetch report from class ReportParser | Parse raw METAR data from a WeatherReport object into actual | values and return the object with the values filled in. | | Methods defined here: | | ParseReport(self, MetarReport=None) | Take report with raw info only and return it with in | parsed values filled in. Note: This function edits the | WeatherReport object you supply! | | __init__(self, MetarReport=None) | Set attribute Report as specified on instantation. | | extractCloudInformation(self) | Extract cloud information. Return None or a tuple (sky type as a | string of text, cloud type (if any) and suggested pixmap name) | | extractSkyConditions(self) | Extract sky condition information from the encoded report. Return | a tuple containing the description of the sky conditions as a | string and a suggested pixmap name for an icon representing said | sky condition. | | match_WeatherPart(self, regexp) | Return the matching part of the encoded Metar report. | regexp: the regexp needed to extract this part. | Return the first matching string or None. | WARNING: Some Metar reports may contain several matching | strings, only the first one is taken into account! class WeatherReport | Incorporates both the unparsed textual representation of the | weather report and the parsed values as soon as they are filled | in by ReportParser. | | Methods defined here: | | __init__(self, MetarStationCode=None) | Clear all fields and fill in wanted station id. | | getCloudinfo(self) | Return a tuple consisting of the parsed cloud information and a | suggest pixmap name | | getCloudtype(self) | Return cloud type information | | getConditions(self) | Return a tuple consisting of the parsed sky conditions and a | suggested pixmap name | | getCycle(self) | Return cycle value. | The cycle value is not the frequency or delay between | observations but the "time slot" in which the observation was made. | There are 24 cycle slots every day which usually last from N:45 to | N+1:45. The cycle from 23:45 to 0:45 is cycle 0. | | getDewPointCelsius(self) | Return dewpoint in degrees Celsius. | | getDewPointFahrenheit(self) | Return dewpoint in degrees Fahrenheit. | | getFullReport(self) | Return the complete weather report. | | getHumidity(self) | Return relative humidity in percent. | | getISOTime(self) | Return the time when the observation was made in ISO 8601 format | (e.g. 2002-07-25 15:12:00Z) | | getPixmap(self) | Return a suggested pixmap name, without extension, depending on | current weather. | | getPressure(self) | Return pressure in hPa. | | getRawMetarCode(self) | Return the encoded weather report. | | getReportURL(self) | Return the URL from which the report was fetched. | | getSkyConditions(self) | Return sky conditions | | getStationAltitude(self) | Return the station's altitude above the sea in meters. | | getStationCity(self) | Return city-part of station name | | getStationCountry(self) | Return country-part of station name | | getStationLatitude(self) | Return the station's latitude in dd-mm[-ss]D format : | dd : degrees | mm : minutes | ss : seconds | D : direction (N, S, E, W) | | getStationLatitudeFloat(self) | Return latitude as a float | | getStationLongitude(self) | Return the station's longitude in dd-mm[-ss]D format : | dd : degrees | mm : minutes | ss : seconds | D : direction (N, S, E, W) | | getStationLongitudeFloat(self) | Return Longitude as a float | | getStationName(self) | Return full station name | | getStationPosition(self) | Return latitude, longitude and altitude above sea level of station | as a tuple. Some stations don't deliver altitude, for those, None | is returned as altitude. The lat/longs are expressed as follows: | xx-yyd | where xx is degrees, yy minutes and d the direction. | Thus 51-14N means 51 degrees, 14 minutes north. d may take the | values N, S for latitues and W, E for longitudes. Latitude and | Longitude may include seconds. Altitude is always given as meters | above sea level, including a trailing M. | Schipohl Int. Airport Amsterdam has, for example: | ('52-18N', '004-46E', '-2M') | Moenchengladbach (where I live): | ('51-14N', '063-03E', None) | If you need lat and long as float values, look at | getStationPositionFloat() instead | | getStationPositionFloat(self) | Return latitude and longitude as float values in a | tuple (lat, long, alt). | | getTemperatureCelsius(self) | Return the temperature in degrees Celsius. | | getTemperatureFahrenheit(self) | Return the temperature in degrees Fahrenheit. | | getTime(self) | Return the time when the observation was made. Note that this | is *not* the time when the report was fetched by us | Format: YYYY.MM.DD HHMM UTC | Example: 2002.04.01 1020 UTC | | getVisibilityKilometers(self) | Return visibility in km. | | getVisibilityMiles(self) | Return visibility in miles. | | getWeather(self) | Return short weather conditions | | getWindCompass(self) | Return wind direction as compass direction | (e.g. NE or SSE) | | getWindDirection(self) | Return wind direction in degrees. | | getWindSpeed(self) | Return the wind speed in meters per second. | | getWindSpeedBeaufort(self) | Return the wind speed in the Beaufort scale | cf. http://en.wikipedia.org/wiki/Beaufort_scale | | getWindSpeedKnots(self) | Return the wind speed in knots | | getWindSpeedMilesPerHour(self) | Return the wind speed in miles per hour. | | getWindchill(self) | Return wind chill in degrees Celsius | | getWindchillF(self) | Return wind chill in degrees Fahrenheit FUNCTIONS metar_to_iso8601(metardate) Convert a metar date to an ISO8601 date. parseLatLong(latlong) Parse Lat or Long in METAR notation into float values. N and E are +, S and W are -. Expects one positional string and returns one float value. DATA CLOUD_RE_STR = '^(CAVOK|CLR|SKC|BKN|SCT|FEW|OVC|NSC)([0-9]{3})(CB)?$' COND_RE_STR = r'^(-|\\+)?(VC|MI|BC|PR|TS|BL|SH|DR|FZ)?(DZ|RA|SN|...S|U... __author__ = 'klausman-pymetar@schwarzvogel.de' __version__ = '0.19' VERSION 0.19 AUTHOR klausman-pymetar@schwarzvogel.de pymetar-0.19/THANKS0000644000175000017530000000260011516603574015520 0ustar klausmanklausman00000000000000Thanks go to... ...Klaus Alexander Seistrup for pointing me to some docs regarding cycles, pointing out a flaw in the way Fahrenheit and Celsius temperatures are handled and for providing valuable feedback regarding packaging. ...Jerome Alet who adapted the ob-parsing code of Spiros Papadimitriou's gnome-weather-applet to Python and helped a lot with debugging and developing this library. ...David Leblanc for confirming a parsing bug ...Davide Di Blasi for suggesting (and implementing) proxy capability ...Tim Middleton who provided code for getWindchill() and getCustom() ...Adrian Holovaty for pointing out trouble with types.TupleType and contributing a solution ...Stefan Majer for pointing out an exception bug ...Uli Martens for providing a Debian package and a man page ...Laurent Rahuel for catching a bug in getVisibilityKilometers() ...Nicolas Évrard for finding a cloud cover bug ...Gregor Hermann for finding another sky condition bug ...Alexander Voronin for finding two long-standing bugs in the the cloud and conditions parsing regexen and suggesting a better method for storing and reporting sky conditions and cloud information. He also contributed code to calculate wind chill if it's not given in the report. ...Jeff Miller for finding a bug in CLOUD_RE_STR (sky condition parsing) and submitting the most complete bug report, patch and test case ever pymetar-0.19/PKG-INFO0000644000175000017530000000216611735301222015675 0ustar klausmanklausman00000000000000Metadata-Version: 1.0 Name: pymetar Version: 0.19 Summary: Pymetar (c) 2002-2012 Tobias Klausmann Pymetar is a python module and command line tool designed to fetch Metar reports from the NOAA (http://www.noaa.gov) and allow access to the included weather information. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Home-page: http://www.schwarzvogel.de/software-pymetar.shtml Author: Tobias Klausmann Author-email: klausman-pymetar@schwarzvogel.de License: GNU GPL Description: UNKNOWN Platform: UNKNOWN pymetar-0.19/COPYING0000644000175000017530000004334211710005370015632 0ustar klausmanklausman00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. pymetar-0.19/README0000644000175000017530000000764311653235453015500 0ustar klausmanklausman00000000000000pymetar - a module to fetch and parse METAR reports --------------------------------------------------- NOTE: If you're looking for information regarding Python 3 and pymetar, see the end of this document. The National Oceanic and Atmospheric Administration (NOAA, www.noaa.gov) provides easy access to the weather reports generated by a large number of weather stations (mostly at airports) worldwide. Those reports are called METAR reports and are delivered as plain text files that look like this: Duesseldorf, Germany (EDDL) 51-18N 006-46E 41M Jul 26, 2002 - 03:50 AM EST / 2002.07.26 0850 UTC Wind: from the SW (220 degrees) at 9 MPH (8 KT):0 Visibility: 3 mile(s):0 Sky conditions: mostly cloudy Weather: mist Temperature: 60 F (16 C) Dew Point: 57 F (14 C) Relative Humidity: 87% Pressure (altimeter): 30.00 in. Hg (1016 hPa) ob: EDDL 260850Z 22008KT 5000 BR SCT006 BKN012 16/14 Q1016 BECMG BKN015 cycle: 9 While this is convenient if you just want to quickly look up the data, there's some effort involved in parsing all of this into a format that is digestible by a program. Plus, you have to remember the base URL of the reports and fetch the file. This is what this library does. All you have to do is find the station you're interested in at http://www.nws.noaa.gov/tg/siteloc.shtml and feed the 4-letter station code to the MetarReport class. On the user end, the library provides a large number of methods to fetch the parsed information in a plethora of formats. Those functions are described in the file librarydoc.txt which was in turn generated using PyDoc. As of version 0.5, pymetar uses urllib2 which in turn makes it easy to honor the environment variable HTTP_PROXY. This simplifies use of a proxy tremendously. Thanks go to Davide Di Blasi for both suggesting and implementing this. The environment variable is easy to use: just set it to: http://username:password@proxy.yourdomain.com:port As of version 0.11, you can also specify a proxy (with the same syntax) as an argument to the fetching function. This is sometimes easier when using PyMETAR in a web application environment (such as ZopeWeatherApplet by Jerome Alet). See librarydoc.txt for details on how to accomplish that. You can also use IPs instead of hostnames, of course. When in doubt, ask your proxy admin. Due to some peculiarities in the METAR format, I can not rule out the possibility that the library barfs on some less common types of reports. If you encounter such a report, please save it and the error messages you get as completely as possible and send them to me at klausman-pymetar@schwarzvogel.de - Thanks a lot! Of course you may send all the other bugs you encounter to me, too. As this is a Python library, chances are that you are Python programmer and can provide a patch. If you do so, please, by all means use Spaces for indentation, four per level, that makes merging the patch a lot easier. Python 3 -------- Pymetar does currently not support Python 3 (Python 2.4-2.7 should be fine. Versions <2.4 are not officially supported but may work by chance). I'm currently in the process of plotting out the future of Pymetar, which naturally includes thinking about Py3. The design of the whole library is now close to eight years old. At this point, there is little that I can do in the ways of changing the API gradually, so I'm thinking about releasing a totally incompatible new library which will only share the basic purpose of Pymetar. I don't think that the current code can be refactored easily and I have meant to try different approaches to the same problem (pylex, dispatcher code, state machines and sundry other stuff). *If* I go that route, it will be Py3-only, since that is the future. Also, I will create a better test suite/scaffolding. If you want to join me in building and designing the "new Pymetar", feel free to join me on the mailing list. Oh, and we need a new name or some other distinctive tag for the new code. And no, it will not be pymetar-ng. pymetar-0.19/pymetar.10000644000175000017530000000307411730600054016342 0ustar klausmanklausman00000000000000.\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "PYMETAR" "1" "February 2011" "" "" . .SH "NAME" \fBpymetar\fR \- query METAR information and display it in a user\-friendly way . .SH "SYNOPSIS" \fBpymetar\fR [station] . .P \fBpymetar\fR \-\-help . .P \fBpymetar\fR \-\-version . .SH "DESCRIPTION" Use pymetar to retrieve METAR (METeorological Aviation Report) information from the internet provided by the NOAA (National Oceanic and Atmospheric Administration) and display it in an plain text format\. . .P Call pymetar with the name of the METAR station you want to query\. You can get the four\-letter specifier of a station near you from http://www\.nws\.noaa\.gov/tg/siteloc\.shtml . .SH "OPTIONS" . .TP \fB\-\-help\fR, \fB\-h\fR Display the usage screen and quit\. . .TP \fB\-\-version\fR, \fB\-v\fR Display the version number of pymetar and the pymetar library and quit\. . .SH "AUTHOR" pymetar was written by Tobias Klausmann \fIklausman\-pymetar@schwarzvogel\.de\fR . .P This manual page was written by Tobias Klausmann using ronn, a Markdown to roff converter\. . .P Permission is granted to copy, distribute and/or modify this document under the same terms as pymetar itself (the GNU General Public License, Version 2 or later)&\. . .SH "ENVIRONMENT" . .TP \fBHTTP_PROXY\fR Set this variable to your HTTP proxy daemon to accelerate consecutive runs of pymetar\. The name of the variable is case insensitive, you can also use http_proxy or HTTP_Proxy if you like\. The syntax for this variable is http://username:password@proxy\.yourdomain\.com:port