Väder i QGIS?

SMHI har mängder av öppna API för att komma åt data av olika slag. I detta inlägg tittar jag närmare på prognosdata och om det är möjligt att bygga in stöd för detta i QGIS på något sätt.

SMHI släpper väldigt mycket data under licensen Creative Commons 4.0 BY, och på http://opendata.smhi.se/apidocs/metfcst så finns mer information om de olika tjänsterna.

Jag är intresserad av prognosdata på valfri geografisk plats i Sverige, så då använder jag ”pmp3g v2”, som du kan läsa mera om på http://opendata.smhi.se/apidocs/metfcst/parameters.html.

I korthet så ger man ett kommando:

GET /api/category/{category}/version/{version}/geotype/point/lon/{longitude}/lat/{latitude}/data.json

Vilket returnerar en mängd data för den angivna punkten. Man kan prova i en vanlig webbläsare också med (exempelvis):

https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/14.97/lat/57.67/data.json

Ovanstående hämtar och visar prognosdata för Eksjö i JSON format.

Det är en ganska stor mängd data organiserad i en ”tidsserie” där det närmaste dygnet anges timme för timme, för att övergå till tre timmars intervall och senare sex och tolv timmar, i upp till tio dygn framåt. Data uppdateras regelbundet, så oavsett när man begär dessa data så skall de vara så aktuella de kan vara.

I tidsserien så finns det runt 20 parametrar för varje tidpunkt där temperatur, vind, relativ fuktighet, regnmängd, etc. För att läsa dessa data och lyfta ut sådant man kan vara intresserad av så behövs någon form av ”tolk”.

Eftersom QGIS helst hanterar data med Python så behövs det ett Python bibliotek som förstår JSON.

lat = 57.67
lon = 14.97
url = 'https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/%s/lat/%s/data.json' % (lon, lat)
import urllib3
import json
http = urllib3.PoolManager()
response = http.request('GET', url)
result = json.loads(response.data.decode('utf-8'))
print(result['timeSeries'][0]['parameters'][11])

Ovanstående skriver ut temperaturdata för den första tidsserien, det vill säga ”nu” på följande sätt:

{'unit': 'Cel', 'name': 't', 'levelType': 'hl', 'level': 2, 'values': [15.9]}

Så det är lika enkelt att enbart skriva ut temperaturen genom att lägga till [‘values’][0] i kommandot, såhär:

print(result['timeSeries'][0]['parameters'][11]['values'][0])

Då finns det en metod för att hämta aktuell temperatur för en bestämd plats. Vilket gör det ganska enkelt att skapa en QGIS process som hämtar temperaturen för en angiven position.

Skärmbild_2017-08-04_10-42-07.png

När ovanstående körs så får man fylla i latitud och longitud, medan funktionen returnerar temperaturen just nu. Den här funktionen är ganska oanvändbar för sig själv då det inte finns någon metod för att fånga upp resultatet. Om man i stället gör det till en ren Pythonfunktion, så kan man baka in det i en pythonprocess som tar ett punktlager som indata, och går igenom alla punkterna samt lägger till temperatur i ett nytt attribut för alla dessa. En sådan funktion skulle kunna se ut såhär:

##Get Temp=name
##Punktlager=vector

from PyQt4.QtCore import QVariant
from qgis.core import QgsVectorLayer, QgsField

def getTemp(lat,lon):
 url ='https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/%s/lat/%s/data.json' % (round(lon,5), round(lat,5))
 import urllib3
 import simplejson
 http = urllib3.PoolManager()
 r = http.request('GET', url)
 result = simplejson.loads(r.data.decode('utf-8'))
 temperatur=result['timeSeries'][0]['parameters'][11]['values'][0]
 return(temperatur)
 
lager = processing.getObjectFromUri(Punktlager)
lager.startEditing()
if (lager.isValid()):
 progress.setText('Lager OK')
else:
 progress.setText('Felaktigt lager')
namn = 'temp'
attribut = [attr.name() for attr in lager.pendingFields()]
try:
 if not namn in attribut:
  res = lager.dataProvider().addAttributes( [ QgsField(namn, QVariant.Double) ] )
  lager.updateFields()
except:
 progress.setText('Attribut temp finns redan, skriver över!')

for punkt in lager.getFeatures():
 geometri = punkt.geometry()
 lat = geometri.asPoint().y()
 lon = geometri.asPoint().x()
 temperatur = getTemp(lat, lon)
 punkt['temp'] = temperatur
 lager.updateFeature(punkt)
 progress.setText('Lat: %s Long: %s Temp: %s C' % (lat, lon, temperatur))
lager.commitChanges()

När jag kör detta skript som process så får jag först välja ett vektorlager, som måste vara oprojicerat med decimala lat/long koordinater, och det bör vara ett punktlager för att det skall fungera.

Skärmbild_2017-08-04_13-16-09.png

Skriptet kontrollerar om det finns ett ”temp” attribut, och lägger till det om så inte är fallet. Till detta attribut hämtas och skrivs aktuell temperatur från SMHI tjänsten.

Du kan själv anpassa skriptet och välja en annan ”timeSeries” för att få exempelvis prognos för i morgon, men även hämta andra värden än temperaturen.

På så sätt skulle man kunna använda QGIS för att skapa en helt egen prognoskarta med symboler och text. Exempelvis att sätta upp på friluftsanläggningen, arbetet, eller liknande, med egen layout och logotyp. Glöm bara inte att ange CC-BY SMHI som källa!

(Exempel på väderkarta i QGIS med prognos för om 24 timmar, från skapande tillfället):

Skärmbild från 2017-08-04 17-15-17.png

[Jag gjorde även en hastig YouTube film om hur kartan ovan är gjord.] https://www.youtube.com/watch?v=0MlgtpuONt4&t=302s

Annonser

Taggar:, ,

One response to “Väder i QGIS?”

  1. Johan T says :

    Tackar! Detta skall bli intressant att prova när tid finns.

Kommentera

Fyll i dina uppgifter nedan eller klicka på en ikon för att logga in:

WordPress.com Logo

Du kommenterar med ditt WordPress.com-konto. Logga ut / Ändra )

Twitter-bild

Du kommenterar med ditt Twitter-konto. Logga ut / Ändra )

Facebook-foto

Du kommenterar med ditt Facebook-konto. Logga ut / Ändra )

Google+ photo

Du kommenterar med ditt Google+-konto. Logga ut / Ändra )

Ansluter till %s

%d bloggare gillar detta: