24.6.9.2 Projekt Wetter-Daten

Nach https://de.wikipedia.org/wiki/Openweathermap ist OpenWeatherMap ein Online-Dienst, der eine programmiersprachen-unabhängige und frei nutzbare Schnittstelle (API) für Wetterdaten bereitstellt. Sie müssen aber über https://home.openweathermap.org/users/sign_up einen kostenfreien API-Key anfordern, um auf diese Wetterdaten zugreifen zu können. Wichtig in Bezug auf das vorgestellte Projekt ist Tatsache, dass alle Wetterdaten im JSON-Format (Standard) bezogen werden können. Als eine Einschränkung für die kostenlose API gilt: Die Wetterdaten werden nur alle zwei Stunden aktualisiert.

Für den Aufruf der Wetterdaten für den Ort Osterburg in Deutschland wurden diese Werte verwendet:

Ort:  q={city name} → q=Osterburg 
Ort und Landeskenner: q={city name},{country code} → q=Osterburg,de 
Daten-Format: mode={ xml | html } JSON ist Standard 
Sprache für kurze Wetterbeschreibung: lang={Landeskenner} → lang=de 
Temperatur-Skala: units={ metric (°C) | imperial (F) } Standard ist Kelvin → units=metric 
API-Key: APIID={ API-Key } → APIID=YourAPIKEY
URL:  http://api.openweathermap.org/data/2.5/weather?q=Osterburg,de&lang=de&units=metric&APPID=yorAPIKEY 

B1 - GUI

Abbildung 24.6.9.2.1: GUI Wetterdaten

Im Projekt müssen folgende Aufgaben bearbeitet werden:

Der Quelltext wird vollständig angegeben und anschließend kommentiert:

[1] ' Gambas class file
[2] 
[3] Public sJSONData As String
[4] Public sAPIKey As String
[5] 
[6] Public jCollection As JSONCollection
[7] Public cWind As Collection
[8] Public cClouds As Collection
[9] Public cMain As Collection
[10] Public cSys As Collection
[11] Public aWeather As Variant[]
[12] Public iTimeStampGMT As Integer
[13] Public sCity As String
[14] 
[15] Const _API_KEY As String = "YOUR_API_KEY"
[16] Const _CITY As String = "YOUR_CITY"
[17] Const _COUNTRY As String = "YOUR_COUNTRY"
[18] 
[19] Public Sub Form_Open()
[20] 
[21]   Dim sFormat, sMessage As String
[22] 
[23]   FMain.Center()
[24]   FMain.Resizable = False
[25]   FMain.Title = ("Current weather data")
[26]   
[27]   sJSONData = GetWeatherData(_CITY, _COUNTRY, _API_KEY)
[28]   
[29] ' Print sJSONData
[30] ' Shell "echo '" & sJSONData & "' | python -m json.tool" To sFormat
[31] ' Print sFormat
[32]   
[33]   jCollection = JSON.Decode(sJSONData, True)  
[34]   
[35]   If jCollection["cod"] = 401 Then
[36]      sMessage = "Sie müssen einen (kostenlosen) API-Key von<br>" 
[37]      sMessage &= "<b>https://home.openweathermap.org/users/sign_up</b>" 
[38]      sMessage &= "<br>anfordern, um die Wetter-Daten auslesen zu können!"
[39]      Message.Error(sMessage)
[40]      FMain.Close()
[41]   Else
[42]       ShowWeatherData()
[43]   Endif  
[44]     
[45] End ' Form_Open()
[46] 
[47] '---------------------------------------------------------------------------------------------------
[48] 
[49] Private Sub ShowWeatherData()
[50]   
[51]   Dim iTimeStampUTC As Integer
[52]   Dim sCityName, sDate, sIcon, sDay, sDateDMY, sTime As String
[53]   Dim dDate As Date
[54]   
[55]   sCityName = jCollection["name"]
[56] 
[57]   iTimeStampUTC = jCollection["dt"]
[58]   dDate = TimeStampToDate(iTimeStampUTC)   
[59]   sDay = Format$(dDate, "dddd")
[60]   sDateDMY = Format$(dDate, "d. mmmm yyyy")
[61]   sTime = Format$(dDate, "hh:nn") & " Uhr (UTC)"     
[62]   sDate = sDay & ", " & sDateDMY & " - " & sTime
[63]   FMain.Text = ("Current weather data") & (" for ") & sCityName & ": " & sDate
[64] 
[65]   cWind = jCollection["wind"]
[66]   txbWindSpeed.Text = cWind["speed"] 
[67]   txbWindDirection.Text = Round(cWind["deg"], 0)
[68]  '-----------------------------------------------
[69] ' cMain = jCollection["main"]
[70] ' txbTemperature.Text = cMain["temp"]
[71] ' txbPressure.Text = cMain["pressure"]
[72] ' txbHumidity.Text = cMain["humidity"]  
[73]   txbTemperature.Text = jCollection["main"]["temp"]
[74]   txbPressure.Text = jCollection["main"]["pressure"]
[75]   txbHumidity.Text = jCollection["main"]["humidity"]    
[76] '----------------------------------------------------
[77]   cClouds = jCollection["clouds"]
[78]   txbCloudiness.Text = cClouds["all"]
[79]  '------------------------------------
[80]   cSys = jCollection["sys"]
[81]   txbSunRise.Text = Format(TimeStampToDate(cSys["sunrise"]), "hh:nn")
[82]   txbSunSet.Text = Format(TimeStampToDate(cSys["sunset"]), "hh:nn")
[83]  '--------------------------------------------------------------------
[84]   aWeather = jCollection["weather"]  
[85]   lblDescription.Text = aWeather[0]["description"]
[86]   sIcon = aWeather[0]["icon"]  
[87] ' lblDescription.Text = jCollection["weather"][0]["description"]
[88] ' sIcon = jCollection["weather"][0]["icon"]
[89]   GetWeatherIcon(sIcon)
[90]   pboxWeather.Picture = Picture[Application.Path &/ "Icon/weathericon.png"]
[91]   
[92] End ' ShowWeatherData()
[93] 
[94] Private Function GetWeatherData(City As String, Country As String, APIKey As String) As String
[95]   
[96]   Dim sURL As String 
[97]   Dim hHTTPClient As HttpClient
[98]   
[99]   sURL = "http://api.openweathermap.org/data/2.5/weather"
[100]     sURL &= "?q=" & City & "," & Country
[101]   sURL &= "&lang=" & Country
[102]   sURL &= "&units=metric" 
[103]   sURL &= "&APPID=" & APIKey 
[104]   
[105]   hHTTPClient = New HttpClient As "hHTTPClient"
[106]   hHttpClient.URL = sURL
[107]   hHTTPClient.Async = False
[108]   hHTTPClient.Timeout = 10
[109]   Try hHTTPClient.Get()
[110] 
[111]   Return hHTTPClient.ReadLine()
[112]   
[113] End ' GetData(City As String, Country As String, APIKey)
[114] 
[115] Private Sub GetWeatherIcon(Icon As String)
[116]   
[117]   Dim hHTTPClient As HttpClient
[118]   
[119]   hHTTPClient = New HttpClient As "hHTTPClient"
[120]   hHttpClient.URL = "http://openweathermap.org/img/w/" & Icon & ".png"
[121]   hHTTPClient.Async = False
[122]   hHTTPClient.Timeout = 10
[123]   Try hHTTPClient.Get([], Application.Path &/ "Icon/weathericon.png")
[124]   
[125] End ' GetWeatherIcon(...)
[126] 
[127] Private Function TimeStampToDate(TimeStamp As Integer) As Date  
[128]   Return DateAdd(CDate("1/1/1970"), gb.Second, TimeStamp)  
[129] End ' TimeStampToDate

Kommentar:

Analysiert man den folgenden JSON-Text

{"coord":{"lon":11.77,"lat":52.78},"weather":[{"id":500,"main":"Rain","description":"leichter Regen",
"icon":"10d"}],"base":"cmc stations","main":{"temp":8.61,"pressure":1021.23,"humidity":92, 
"temp_min":8.61,"temp_max":8.61,"sea_level":1025.91,"grnd_level": 1021.23},"wind":{"speed":3.66, "deg":90.505},
"rain":{"3h":0.84},"clouds":{"all":92},"dt":1460443601,"sys": {"message":0.0036, "country":"DE","sunrise":1460434806,
"sunset":1460484485},"id":2856639,"name":"Osterburg","cod":200}

dann erkennt man für die einzelnen Keys in der JSONCollection neben den Werten der Wetter-Daten auch den Werte-Typ, die hier noch einmal explizit angegeben werden:

Anzahl der Elemente in der JSON-Collection: 11 
----------------------------------------------------- 
Schlüssel 1 : "coord" ---> Wert-Typ: JSONCollection 
Schlüssel 2 : "weather" ---> Wert-Typ: Variant[] 
Schlüssel 3 : "base" ---> Wert-Typ: String 
Schlüssel 4 : "main" ---> Wert-Typ: JSONCollection 
Schlüssel 5 : "wind" ---> Wert-Typ: JSONCollection 
Schlüssel 6 : "clouds" ---> Wert-Typ: JSONCollection 
Schlüssel 7 : "dt" ---> Wert-Typ: Integer 
Schlüssel 8 : "sys" ---> Wert-Typ: JSONCollection 
Schlüssel 9 : "id" ---> Wert-Typ: Integer 
Schlüssel 10 : "name" ---> Wert-Typ: String 
Schlüssel 11 : "cod" ---> Wert-Typ: Integer

Beispiel 1: Ermittlung des Datums und der Zeit, zu der die Wetter-Daten erfasst wurden

Der Schlüssel 7 „dt“ steht für DateTime und liefert den Wert zu diesem Schlüssel als Zeit-Stempel (TimeStamp) vom Typ Integer. Wie Sie erkennen können, wurde das vollständige Datum auf eine ganze Zahl abgebildet. In den Zeilen 58 bis 60 erfolgt mit Hilfe der Funktion TimeStampToDate(..) die Konvertierung des Zeitstempels in ein Datum (Typ Date), das für die Anzeige in der Fenster-Zeile dreifach anders formatiert wird:

iTimeStampUTC = jCollection["dt"]
dDate = TimeStampToDate(iTimeStampUTC)   
sDay = Format$(dDate, "dddd")
sDateDMY = Format$(dDate, "d. mmmm yyyy")
sTime = Format$(dDate, "hh:nn") & " Uhr (UTC)"     
sDate = sDay & ", " & sDateDMY & " - " & sTime
FMain.Text = ("Current weather data") & (" for ") & sCityName & ": " & sDate

Beispiel 2: Ermittlung Temperatur, Luftdruck und Luftfeuchtigkeit

Der Schlüssel 4 „main“ liefert als Wert eine Collection mit den Keys „temp“, „pressure“ und „humidity“. Die Werte für Temperatur, Luftdruck und Luftfeuchtigkeit können Sie entweder mit Hilfe der Variablen cMain in den Zeilen 2 bis 4 sofort anzeigen oder Sie verwenden die Zeilen 5 bis 7:

[1] ' cMain = jCollection["main"]
[2] ' txbTemperature.Text = cMain["temp"]
[3] ' txbPressure.Text = cMain["pressure"]
[4] ' txbHumidity.Text = cMain["humidity"]  
[5]   txbTemperature.Text = jCollection["main"]["temp"]
[6]   txbPressure.Text = jCollection["main"]["pressure"]
[7]   txbHumidity.Text = jCollection["main"]["humidity"] 

Beispiel 3: Ermittlung der Wetter-Beschreibung und des Bezeichners für das Wetter-Icon

ICON

Der Schlüssel 2 „weather“ in der JSONCollection liefert als Wert in diesem Fall ein Variant-Array dessen einziges Element eine Collection ist! Da hier nur die Wetter-Beschreibung und der Bezeichner für das Wetter-Icon interessieren, werden die beiden Keys „description“ und „icon“ benötigt. Unter Verwendung der Variablen aWeather vom Typ Variant[] können Sie die Wetter-Beschreibung sofort anzeigen. Die Zeile 3 in Verbindung mit den Zeilen 6 und 7 lädt die Datei für das Wetter-Icon und zeigt das Wetter-Icon unmittelbar in einer Picture-Box an. Eine Alternative für die Zeilen 1 bis 3 – unter Verzicht auf eine Variable für das Variant-Array – finden Sie in den Zeilen 4 und 5:

[1]   aWeather = jCollection["weather"]  
[2]   lblDescription.Text = aWeather[0]["description"]
[3]   sIcon = aWeather[0]["icon"]  
[4] ' lblDescription.Text = jCollection["weather"][0]["description"]
[5] ' sIcon = jCollection["weather"][0]["icon"]
[6]   GetWeatherIcon(sIcon)
[7]   pboxWeather.Picture = Picture[Application.Path &/ "Icon/weathericon.png"]

Hier sehen Sie den gut lesbaren JSON-Text, weil er formatiert ausgegeben wird:

{
    "base": "cmc stations", 
    "clouds": {
        "all": 92
    }, 
    "cod": 200, 
    "coord": {
        "lat": 52.78, 
        "lon": 11.77
    }, 
    "dt": 1460443601, 
    "id": 2856639, 
    "main": {
        "grnd_level": 1021.23, 
        "humidity": 92, 
        "pressure": 1021.23, 
        "sea_level": 1025.91, 
        "temp": 8.61, 
        "temp_max": 8.61, 
        "temp_min": 8.61
    }, 
    "name": "Osterburg", 
    "rain": {
        "3h": 0.84
    }, 
    "sys": {
        "country": "DE", 
        "message": 0.0036, 
        "sunrise": 1460434806, 
        "sunset": 1460484485
    }, 
    "weather": [
        {
            "description": "leichter Regen", 
            "icon": "10d", 
            "id": 500, 
            "main": "Rain"
        }
    ], 
    "wind": {
        "deg": 90.505, 
        "speed": 3.66
    }
}

Wenn in der Wetter-Beschreibung zum Beispiel Umlaute enthalten sind, so werden diese konvertiert:

"weather": [
  {
    "description": "\u00fcberwiegend bew\u00f6lkt", 
    "icon": "04d", 
    "id": 803, 
    "main": "Clouds"
  }
]

Auch die Fehlermeldungen erhalten Sie im JSON-Format – wie hier bei einem fehlendem API-Key:

{ 
    "cod": 401, 
    "message": "Invalid API key. Please see http://openweathermap.org/faq#error401 for more info." 
}

Dieser Fehler wird auch im Projekt in den Zeilen 35 bis 43 abgefangen:

If jCollection["cod"] = 401 Then
   sMessage = "Sie müssen einen (kostenlosen) API-Key von<br>" 
   sMessage &= "<b>https://home.openweathermap.org/users/sign_up</b>" 
   sMessage &= "<br>anfordern, um die Wetter-Daten auslesen zu können!"
   Message.Error(sMessage)
   FMain.Close()
Else
   ShowWeatherData()
Endif  

B2

Abbildung 24.6.9.2.2: Fehlermeldung

Das vorgestellte Projekt ist eine Adaption eines Projektes von Steve Dee, das er auf seiner Website http://captainbodgit.blogspot.de vorgestellt hat.

Download