Pagina 1 van 1

Lazarus Pascal – Ophalen van Yahoo Weersvoorspelling

Lazarus Pascal – Ophalen van Yahoo Weersvoorspelling
   0

Zoals een aantal bezoekers al weet; ik prul graag met Lazarus Pascal, een gratis ontwikkelomgeving voor het ontwikkelen van programma’s voor Windows, MAcOS X en Linux, wat veel weg heeft van Delphi.

In een van mijn projecten wilde ik de weersvoorspelling van Yahoo integreren. Ik moest dus uitvogelen hoe je de Yahoo Weer API kunt gebruiken en omdat het me even moeite koste, leek het me handig voor anderen om er een kleine unit voor te maken. Je kunt de unit gratis gebruiken, of er van leren hoe het e.e.a. met Yahoo Weer werkt.

Deze unit gebruikt Synapse, wat gratis is, en fcl-json, wat standaard al bij Lazarus zit.




De Yahoo Weersvoorspelling gebruiken …

Zolang Yahoo deze dienst aanbiedt, kunnen we de weersvoorspelling van iedere locatie via de Yahoo Weather API ophalen.

Net zoals met zo veel dingen, relatief makkelijk,… als je eenmaal weet hoe het werkt…

SQL-achtige Weer Query

Als je naar de Yahoo Weather Developer Network pagina gaat, dan zie je dat we een SQL-achtige query taal kunnen gebruiken voor het ophalen van de weersverwachting.
Als je een beetje bekend bent met SQL, dan klinkt dit vast bekend:

SELECT * FROM weather.forecast WHERE woeid IN (SELECT woeid FROM geo.places(1) WHERE text="breda,nederland")

Hier halen we de weersvoorspelling (forecast) op van Breda,Nederland, waarbij we Yahoo laten uitzoeken wat de locatie ID is via een subquery:

SELECT woeid FROM geo.places(1) WHERE text="breda,nederland"

Het gevonden antwoord, “woeid”, hebben we in de query nodig om aan te geven voor welke locatie we de weersvoorspelling willen ophalen uit de weather.forecast tabel.

Weersvoorspelling: JSON of XML?

We kunnen de Yahoo Weersvoorspelling ophalen in 1 van de volgende formaten: JSON of XML. Ik kies het liefste JSON, want daar kan ik met weinig moeite en overhead mee aan de slag. XML heeft me in het verleden al te veel hoofdpijn gegeven, dus ik gebruik XML liever niet.

URL opbouwen voor de Yahoo Weersvoorspellingen

Laten we eens gaan kijken naar de link voor het ophalen van de Yahoo Weersvoorspelling, zoals je die ziet onder aan de Yahoo Weather Developer pagina:


https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22breda%2C
%20nederland%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys

Vergeet het volgende dus niet:

  • We willen de weersvoorspelling van “Breda, Nederland“,
  • En wel in JSON formaat

 

De basis URL (link) is:

https://query.yahooapis.com/v1/public/yql?q=...

Dit wordt gevolgd door de SQL-achtige query, welke in in HTML veilige karakters wordt uitgedrukt (ook wel URL Encoding, of Percent-encoding, genoemd, waarbij dus bijvoorbeeld een wordt vervangen door een %20, wat staat voor hex 20, wat decimaal 32 is, en in ASCII code staat voor en spatie).

select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22breda%2C%20nederland%22)...

Zie jet het stukje text%3D%22breda%2C%20nederland%22?
“Vertaald” staat hier: test=”breda, nederland”

Hier kunnen we dus de locatie als tekst doorgeven. De spatie voor “nederland” hoeft er overigens niet te staan, en hoofdletter maakt ook niets uit.
Echter als jouw plaatsnaam, of land naam, spaties bevat, dan moeten we dit dus met URL Encode behandelen. Om nu zeker te zijn dat we niet tegen problemen aanlopen, heb ik dus een functie gemaakt, die in de unit zit, die dit automatisch voor ons doet.

Overigens, soms moet je wat specifieker zijn, als het om de locatie gaat.

Stel je wilt het weer weten voor “Osceola, WI” (WI = Wisonsin, en dat is in de USA). Als we nu “osceola,usa” opgeven, dan lopen we teen een probleem aan. Amerika kent namelijk meerdere plaatsen met de naam Osceola. Omdat we in de subquery aangeven dat we alleen het eerste resultaat willen voor de locatie, dan zullen we zien dat het weer terug komt voor “osecola,fl” – in Florida dus. In dat geval moeten we dus: “osceola,wi,usa” gebruiken (“usa” hoeft er niet eens bij te staan).

Voor de meeste plaatsen is echter “plaats,land” voldoende, zoals we dus doen met “breda,nederlands”.
Hierbij mag het land in het Engels weergegeven worden (Netherlands) of in de taal van het land (Nederland).

Na opgegeven van de locatie, moeten we ook opgegeven in welk format we de data willen (JSON!).

&format=json...

Dit wordt dan gevolgt door wat extra parameters waar we hier maar niet op ingaan:

&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys

We weten dus nu hoe de link (URL) er uitziet en wat wat is.

De Yahoo Weather Forecast Unit

Zoals ik dus al in het begin zei; ik heb alles in een unit gemikt, om het net even wat gemakkelijker te maken. Uiteraard is er altijd ruimte voor verbetering, maar het geeft je een idee en misschien een goede start, mocht je het e.e.a. anders of efficiënter willen doen. Laat het hier vooral weten als je een betere aanpak weet.

Yahoo Weersvoorspelling in een Record

Om het resultaat eenvoudiger te lezen, heb ik besloten alles in een record te zetten.

De JSON data wordt automatisch uit elkaar getrokken en in dit record type (TYahooForecast) gezet. Omdat de weersvoorspelling van Yahoo meteen 10 dagen voorspelt, heb ik nog een record type gemaakt die elk een dag bevatten (zie de array in de TYahooForcast hieronder).


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
  TDayForcast = record
    code:string;
    date:string;
    day:string;
    high:string;
    low:string;
    text:string;
  end;

  TYahooForecast = record
    success:boolean;
    distance:string;
    pressure:string;
    speed:string;
    temperature:string;
    location_city:string;
    location_country:string;
    location_region:string;
    wind_chill:string;
    wind_direction:string;
    wind_speed:string;
    atmosphere_humidity:string;
    atmosphere_pressure:string;
    atmosphere_rising:string;
    atmosphere_visibility:string;
    astronomy_sunrise:string;
    astronomy_sunset:string;
    location_latitude:string;
    location_longitude:string;
    last_updated:string;
    current_code:string;
    current_date:string;
    current_temp:string;
    current_text:string;
    forecast: array [0..9] of TDayForcast;
  end;

Om nu de weersvoorspelling te krijgen, kunnen we een van deze methoden gebruiken:


1
2
3
4
5
6
7
8
// Standaard (in Celsius)
tmpData := GetYahooForecast('breda, nederlands');

// forceer Fahrenheit
tmpData := GetYahooForecast('breda, nederland','f');

// forceer Celsius
tmpData := GetYahooForecast('breda, nederland','c');

Je kunt speciale karakters gewoon gebruiken omdat ik URL Encoding automatisch toepas. In jouw eigen code hoef je hier dus niets mee te doen en hoef je er dus helemaal niet op te letten.

Na het aanroepen, moeten we eerst even controleren of alles goed is gegaan door naar de waarde van het veld “success” in de record te kijken.


1
2
3
4
5
6
7
8
9
10
11
  if tmpData.success then
    begin
      with tmpData do
        begin
          memo1.lines.add('Afstand eenheden: '+distance);
          memo1.lines.add('Luchtdruk eenheden: '+pressure);
          ...
        end;
    end
  else
    // DATA OPHALEN MISLUKT

Zoals je hier al ziet, is het lezen van de data makkelijk geworden.

Voor het lezen vand e voorspelling voor de 10 dagen:


1
2
3
4
5
6
7
8
9
for Counter:=0 to 9 do
  begin
    memo1.lines.add('Code: '+tmpdata.forecast[Counter].code);
    memo1.lines.add('Datum: '+tmpdata.forecast[Counter].date);
    memo1.lines.add('Weekdag: ' +tmpdata.forecast[Counter].day);
    memo1.lines.add('Hoog: '+tmpdata.forecast[Counter].high);
    memo1.lines.add('Laag: ' +tmpdata.forecast[Counter].low);
    memo1.lines.add('Tekst: '+tmpdata.forecast[Counter].text);
  end;

De Unit

Hieronder vindt je de hele unit.
Niet vergeten dus dat je Synapse, nodig hebt, wat gratis is. Je hoeft het niet te installeren, op de Mac faalde dat zelfs. Je hoeft de zip alleen maar uit te pakken en alle bestanden in de directory “/source/lib” in jouw project directory te kopiëren.

Je kunt onderstaande code kopiëren en plakken, of je kunt het hier ook gewoon downloaden (er zit dan een klein voorbeeld programma bij).

Update: HTTPS voor Windows gebruikers … 

Omdat het HTTPS protocol gebruikt wordt, hebben we SSL ondersteuning nodig.
Onder MacOS X en Linux, staat OpenSSL al standaard beschikbaar en kan Synapse daar meten mee aan de slag. Onder Windows is dat helaas niet het geval. Met dank aan Joseph Kaminchick voor het ontdekken van dit probleem! 

Voor Windows gebruikers zitten nu precompiled OpenSSL tools in de ZIP (libeay32.dll, openssl.exe en ssleay32.dll) .

Bedenk dat ik dit alleen met 32 bits applicaties heb getest.

DOWNLOAD - LazarusYahooWeatherUnit 

Platform: Windows, Mac OS X, Linux
Bestand: LazarusYahooWeatherUnit.zip
Versie: 1.1
Omvang: 1.4 MiB
Datum: 17 dec 2016
 Download Nu 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
unit YahooWeather;

{  Unit to retrieve Yahoo Weather Forecast

   (C)2015 Hans Luijten, //www.tweaking4all.com
   Version 1.0

   Uses:  Synapse (http://synapse.ararat.cz/,
          fpjson  (Comes with Lazarus)
          openssl (part of synapse, weather is retrieved with https://)

   Common use:
          GetYahooForecast(location);

          Retrieves current weather and weather forecast, and returns this in a record for easy access.
}


{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, httpsend, ssl_openssl, fpjson, jsonparser;

type
  TDayForcast = record
    code:string;
    date:string;
    day:string;
    high:string;
    low:string;
    text:string;
  end;

  TYahooForecast = record
    success:boolean;
    distance:string;
    pressure:string;
    speed:string;
    temperature:string;
    location_city:string;
    location_country:string;
    location_region:string;
    wind_chill:string;
    wind_direction:string;
    wind_speed:string;
    atmosphere_humidity:string;
    atmosphere_pressure:string;
    atmosphere_rising:string;
    atmosphere_visibility:string;
    astronomy_sunrise:string;
    astronomy_sunset:string;
    location_latitude:string;
    location_longitude:string;
    last_updated:string;
    current_code:string;
    current_date:string;
    current_temp:string;
    current_text:string;
    forecast: array [0..9] of TDayForcast;
  end;

function GetYahooWeatherData(Location:string; AsJSON:boolean =true; TemperatureUnits:string = 'c'):string;
function GetYahooForecast(Location:string; TemperatureUnits:string = 'c'):TYahooForecast;
function YahooURLEncode(s: string): string;

implementation

{
  URLEncode

  Encodes string to be suitable as an URL.

  Example: myencodeURL:=YahooURLEncode(myURL);
}

function YahooURLEncode(s: string): string;
var
  i: integer;
  source: PAnsiChar;
begin
  result := '';
  source := pansichar(s);
  for i := 1 to length(source) do
    if not (source[i - 1] in ['A'..'Z', 'a'..'z', '0'..'9', '-', '_', '~', '.', ':', '/']) then
      result := result + '%' + inttohex(ord(source[i - 1]), 2)
    else
      result := result + source[i - 1];
end;

{ Retrieve Yahoo Weather with forcast and place it in a record for easy access

  MyYahooForcast := GetYahooForecast('town,country',true,'c')
}

function GetYahooForecast(Location:string; TemperatureUnits:string = 'c'):TYahooForecast;
var JSONData:TJSONData;
    RawData:string;
    tmpData:TYahooForecast;
    Counter:integer;
begin
  RawData := GetYahooWeatherData(Location,true,TemperatureUnits);

  Counter:=0;
  while ((RawData='') or (pos('ERROR',RawData)>0)) and (Counter<3) do
    begin
      RawData := GetYahooWeatherData(Location,true,TemperatureUnits);
      inc(Counter);
    end;

  if ((RawData='') or (pos('ERROR',RawData)>0)) then
    tmpData.success:=false
  else
    begin
      JSONData := GetJSON(RawData);

      with tmpdata do
        begin
          success:=true;

          distance:=JSONData.FindPath('query.results.channel.units.distance').AsString;
          pressure:=JSONData.FindPath('query.results.channel.units.pressure').AsString;
          speed:=JSONData.FindPath('query.results.channel.units.speed').AsString;
          temperature:=JSONData.FindPath('query.results.channel.units.temperature').AsString;

          location_city:=JSONData.FindPath('query.results.channel.location.city').AsString;
          location_country:=JSONData.FindPath('query.results.channel.location.country').AsString;
          location_region:=JSONData.FindPath('query.results.channel.location.region').AsString;

          wind_chill:=JSONData.FindPath('query.results.channel.wind.chill').AsString;
          wind_direction:=JSONData.FindPath('query.results.channel.wind.direction').AsString;
          wind_speed:=JSONData.FindPath('query.results.channel.wind.speed').AsString;

          atmosphere_humidity:=JSONData.FindPath('query.results.channel.atmosphere.humidity').AsString;
          atmosphere_pressure:=JSONData.FindPath('query.results.channel.atmosphere.pressure').AsString;
          atmosphere_rising:=JSONData.FindPath('query.results.channel.atmosphere.rising').AsString;
          atmosphere_visibility:=JSONData.FindPath('query.results.channel.atmosphere.visibility').AsString;

          astronomy_sunrise:=JSONData.FindPath('query.results.channel.astronomy.sunrise').AsString;
          astronomy_sunset:=JSONData.FindPath('query.results.channel.astronomy.sunset').AsString;

          location_latitude:=JSONData.FindPath('query.results.channel.item.lat').AsString;
          location_longitude:=JSONData.FindPath('query.results.channel.item.long').AsString;

          last_updated:=JSONData.FindPath('query.results.channel.item.pubDate').AsString;

          current_code:=JSONData.FindPath('query.results.channel.item.condition.code').AsString;
          current_date:=JSONData.FindPath('query.results.channel.item.condition.date').AsString;
          current_temp:=JSONData.FindPath('query.results.channel.item.condition.temp').AsString;
          current_text:=JSONData.FindPath('query.results.channel.item.condition.text').AsString;

          for Counter:=0 to 9 do
            begin
              with tmpData.forecast[Counter] do
                begin
                  code:=JSONData.FindPath('query.results.channel.item.forecast['+IntToStr(Counter)+'].code').AsString;
                  date:=JSONData.FindPath('query.results.channel.item.forecast['+IntToStr(Counter)+'].date').AsString;
                  day:=JSONData.FindPath('query.results.channel.item.forecast['+IntToStr(Counter)+'].day').AsString;
                  high:=JSONData.FindPath('query.results.channel.item.forecast['+IntToStr(Counter)+'].high').AsString;
                  low:=JSONData.FindPath('query.results.channel.item.forecast['+IntToStr(Counter)+'].low').AsString;
                  text:=JSONData.FindPath('query.results.channel.item.forecast['+IntToStr(Counter)+'].text').AsString;
                end;
            end;
       end;
    end;

  GetYahooForecast := tmpData;
end;

{ Retrieve Yahoo Weather information

  myString := GetYahooWeatherData('town,country',true,'c');

  Location: typically use town,country, for example: 'osceola,wi.usa', 'amsterdam,netherlands', 'breda,nederland'
  AsJSON  : return as a JSON string if TRUE, otherwise in XML format (JSON default)
  TemperatureUnits: 'c' for Celcius, and 'f' for Fahrentheit         (C default)
}

function GetYahooWeatherData(Location:string; AsJSON:boolean =true; TemperatureUnits:string = 'c'):string;
var HTTPSender : THTTPSend;
    tmpData : TStringList;
    resultString, resultFormat: String;
begin
  if Location='' then
    GetYahooWeatherData:='ERROR: Missing Location'
  else
    begin
      if AsJSON then
        resultFormat := 'json'
      else
        resultFormat := 'xml';

      tmpData    := TStringList.Create;
      HTTPSender := THTTPSend.Create;

      try
        HTTPSender.HTTPMethod('GET',
                              'https://query.yahooapis.com/v1/public/yql?'+
                                'q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20('+                           // get forecast
                                  'select%20woeid%20from%20geo.places(1)%20where%20text%3D%22'+YahooURLEncode(Location)+'%22)'+              // find location based on string
                                '%20and%20u%3D%22'+TemperatureUnits+'%22'+                                                   // force units (c or f)
                                '&format='+resultFormat+'&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys');

        if (HTTPSender.ResultCode >= 100) and (HTTPSender.ResultCode<=299) then
          tmpData.LoadFromStream(HTTPSender.Document)
      finally
        HTTPSender.Free;
      end;

      if tmpData.Text='' then
        resultString:='ERROR: Unable to retrieve data'
      else
        resultString:=tmpData.Text;

      tmpData.Free;

      GetYahooWeatherData := resultString;
    end;
end;

end.

Extra Info: Yahoo Weer Codes

Yahoo Weather geeft ook een weer-code terug. Het wordt gebruikt om de weer-conditie in tekst weer te geven, vooral als je met andere talen gaat werken. Voor de Engelse taal doet Yahoo dat al zelf in het “text” veld (voor elk van de 10 dagen) en “current_text” veld (voor vandaag).

Hieronder een lijstje met de Engels tekst, welke je zou kunnen ombouwen naar b.v. Nederlands.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
const YahooWeatherCondition : array[0..48] of string = ('tornado',
                                                        'tropical storm',
                                                        'hurricane',
                                                        'severe thunderstorms',
                                                        'thunderstorms',
                                                        'mixed rain and snow',
                                                        'mixed rain and sleet',
                                                        'mixed snow and sleet',
                                                        'freezing drizzle',
                                                        'drizzle',
                                                        'freezing rain',
                                                        'showers',
                                                        'showers',
                                                        'snow flurries',
                                                        'light snow showers',
                                                        'blowing snow',
                                                        'snow',
                                                        'hail',
                                                        'sleet',
                                                        'dust',
                                                        'foggy',
                                                        'haze',
                                                        'smoky',
                                                        'blustery',
                                                        'windy',
                                                        'cold',
                                                        'cloudy',
                                                        'mostly cloudy (night)',
                                                        'mostly cloudy (day)',
                                                        'partly cloudy (night)',
                                                        'partly cloudy (day)',
                                                        'clear (night)',
                                                        'sunny',
                                                        'fair (night)',
                                                        'fair (day)',
                                                        'mixed rain and hail',
                                                        'hot',
                                                        'isolated thunderstorms',
                                                        'scattered thunderstorms',
                                                        'scattered thunderstorms',
                                                        'scattered showers',
                                                        'heavy snow',
                                                        'scattered snow showers',
                                                        'heavy snow',
                                                        'partly cloudy',
                                                        'thundershowers',
                                                        'snow showers',
                                                        'isolated thundershowers',
                                                        'not available');

Met een simpele procedure zou je de code (een nummer) dan kunnen “vertalen”.
Overigens, Codes > 48 zijn altijd “niet beschikbaar” (not available).


1
2
3
4
5
6
7
8
9
10
11
12
13
14
function WeatherCode2String(code:string):string;
var intCode:integer;
begin
  try
    intCode:=StrToInt(code)
  except
    intCode:=3200
  end;

  if (intCode<0) or (intCode>47) then
    WeatherCode2String:= YahooWeatherCondition[48]
  else
    WeatherCode2String:= YahooWeatherCondition[intCode]
end;

 

Donatie Opties


Donaties worden zeer gewaardeerd, maar zijn zeker niet vereist. Donaties worden gebruikt voor het dekken van kosten voor web-hosting en project materialen, en eventueel voor een drankje of een snack. Voor wie al gedoneerd heeft: Hartelijk dank! Het is werkelijk geweldig om te zien dat men onze artikelen en applicaties waardeert.

Reacties


Er zijn nog geen reacties geplaatst.
Je kunt jouw eigen opmerkingen plaatsen m.b.v. dit formulier, of een reactie op een opmerking plaatsen door op de "Reageer" knop te klikken.



Jouw Reactie ...

Vriendelijk verzoek om hier geen lange teksten te plaatsen (zoals source codes, log files of config files). Gebruik daarvoor het Forum.

Deel met anderen:
*
*
Houd me op de hoogte van nieuwe reacties (email).
       Gebruik jouw RSS reader om reacties te volgen.


Tweaking4All uses the free Gravatar service for Avatar display.
Tweaking4All zal nooit jouw email adres met anderen delen.