Minimalistische Regenwarnung, Schweiz (Python)


import numpy as np
import io, urllib.request
from PIL import Image, ImageFilter, ImageFile

plz = 8000 # This one is for Zurich. Replace with the postcode of your liking

weatherDiagramPng = "http://meteo.search.ch/images/chart/%s.png?show=time,temp,rain&lang=en" %(plz)
with urllib.request.urlopen(weatherDiagramPng) as url:
    fd = url.read()
p = ImageFile.Parser()
p.feed(fd)
rainGif = p.close()

npGif = np.asarray(rainGif)
npGifStripe = npGif[174:175,37:725,:] #lowest stripe indicating possible rain, [0  48 128] is the RGB for rain

idx = (npGifStripe[:,:,0] == 0) * (npGifStripe[:,:,1] == 48) * (npGifStripe[:,:,2] == 128)
rainPixels= npGifStripe[idx]
if len(rainPixels)>0:
    print("Regen im Anmarsch!")


Was willst du uns mitteilen?.. das es da etwas gibt… in Python geschrieben…

Das Forum in dem du gepostet hast heißt „Nützliche PHP Skripte“

Kommt da noch was?

Wetterbild-Analyse in 23 Zeilen! Vielleicht bekehre ich noch ein Paar PHP-Abtrünnige…

Das ist so angekommen. Jedoch nicht in der Sprache die IPS kann.
Ich verschiebe es deshalb mal ins Skripte-Forum, bis du die PHP-Übersetzung nachlieferst :wink:

Die PHP-Uebersetzung braucht 120 Zeilen… :slight_smile:

Hier ist also eine (etwas weniger minimalistische) Variante, welche eine punktgenaue Auswertung des Schweizer Niederschlagradars ermöglicht, mit IPS via JSON-RPC kommuniziert und IPS-Variablen setzt, und eine schön formattierte HTML-email schickt mit Durchschnitt, Maximalwert, und Standardabweichung der Niederschlagsdaten. Ein Postgres-Backend wird vorausgesetzt.


import time, datetime
from time import strftime
import numpy as np
import io, urllib.request, socket
import requests, json
import email
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText


import psycopg2, numpy as np
import pylab as plt
import matplotlib.pyplot as plt
import os, sys
import socket
import csv

from PIL import Image, ImageFilter, ImageFile, ImageDraw, ImageFont

# coordinates of epicenter, radius of area
homeX = 154
homeY = 272
rainDataArray = [[31,19328,'RainIntegralLarge'],  # radius, IPS variable ID
                 [10,24618,'RainIntegralMedium'],  # radius, IPS variable ID
                 [1, 36456,'RainIntegralSmall']]  # radius, IPS variable ID

#------------------------------
# Connect to Postgres
def connectIpsDatabase():

    try:
        conn = psycopg2.connect("dbname='IpsLogging' user='ips' host='10.10.10.6' password='pass'")
        pgHost = '10.10.10.6'
    except:
        try:
            conn = psycopg2.connect("dbname='IpsLogging' user='ips' host='whatever.com' password='pass'")
            pgHost = 'whatever.com'
        except:
            try:
                conn = psycopg2.connect("dbname='IpsLogging' user='ips' host='localhost' password='pass'")
                pgHost = 'localhost'
            except:
                print (" - unable to connect to the database")
                return
    print("Postgres connection to: " + pgHost)
    cur = conn.cursor()
    return (cur, conn)
#------------------------------------
# insert records into postgres

def appendIntoPostgres(rainDataArray):
        sqlStatement = """INSERT INTO "WeatherServiceInternet" \
            ("RainIntegralLarge",
            "RainIntegralSmall",
            "RainIntegralMedium",
            "RainIntegralLarge_Max",
            "RainIntegralSmall_Max",
            "RainIntegralMedium_Max")
        VALUES
            (%g,%g,%g,%g,%g,%g);
            """ \
            % (rainDataArray[0][3][0],rainDataArray[0][3][2],
               rainDataArray[1][3][0],rainDataArray[1][3][2],
               rainDataArray[2][3][0],rainDataArray[2][3][2],)

        print("sqlStatement" + sqlStatement)
        cur, conn = connectIpsDatabase()
        try:
            cur.execute(sqlStatement)
            conn.commit()
        except:
            exceptionSql = str(sys.exc_info()[1])[:46]
            print (sys.exc_info())
            #if exceptionSql[:-45] is "duplicate key value violates unique constrain":
            if (exceptionSql == "duplicate key value violates unique constraint"):
                print(ipBikeFile + " is already filed")

#-----------------------------------
def PrintMatrix(matrixToBePrinted): # this function prints 2d arrays as pretty tables
    s = [[str(e) for e in row] for row in matrixToBePrinted]
    lens = [max(map(len, col)) for col in zip(*s)]
    fmt = '	'.join('{{:{}}}'.format(x) for x in lens)
    table = [fmt.format(*row) for row in s]
    print ('
'.join(table))
#------------------------------------
# send string via JSON-RPC
def IpsRpc(methodIps, paramIps):
    url = "http://InsertRegisteredUserHere:InsertPasswordHere@10.10.10.16:2081/api/"
    headers = {'content-type': 'application/json'}

    payload = {
        "method": methodIps,
        "params": paramIps,
        "jsonrpc": "2.0",
        "id": 0,
    }
    response = requests.post(
        url, data=json.dumps(payload), headers=headers).json()
    print(response)

#--------------------------------------
# send email
def sendEmail(sender, receivers, subject, messagebody, attachment, rainDataArray):
    sender = 'InternetWetterRobot@whatever.com'
    receivers = ['InsertRegisteredUserHere']
    # me == my email address
    # you == recipient's email address
    me = 'InternetWetterRobot@whatever.com'
    you = 'InsertRegisteredUserHere'

    # Create message container - the correct MIME type is multipart/alternative.
    msg = MIMEMultipart('alternative')
    msg['Subject'] = "Link"
    msg['From'] = me
    msg['To'] = you

    # Create the body of the message (a plain-text and an HTML version).
    text = "null"
    print(rainDataArray)
    rainHtmlTable = """\
        <html>
        <head></head><body>
        <table width="459" height="211" border="1"><caption><em>Rain distribution</em>
  <br /></caption>
            <tr>
              <th width="76" scope="col"> </th>
              <th width="79" scope="col">average</th>
              <th width="8" scope="col">standard<br />deviation</th>
              <th width="127" scope="col">maximum</th>
            </tr><tr><th scope="row">small</th>
              <td><div align="center"><div align="center">%g </div></div>
              </td><td><div align="center">%g </div>
              </td><td><div align="center">%g </div></td>
            </tr><tr><th scope="row">medium</th>
              <td><div align="center">%g </div>
              </td><td><div align="center">%g </div>
              </td><td><div align="center">%g&nbsp</div>
              </td>
            </tr><tr><th scope="row">large</th>
              <td><div align="center">%g </div>
              </td><td><div align="center">%g </div>
              </td><td><div align="center">%g </div></td>
            </tr>
          </table>
      </body>
    </html>
    """ \
        % (rainDataArray[0][3][0],rainDataArray[0][3][1],rainDataArray[0][3][2],
        rainDataArray[1][3][0],rainDataArray[1][3][1],rainDataArray[1][3][2],
        rainDataArray[2][3][0],rainDataArray[2][3][1],rainDataArray[0][3][2])



    # Record the MIME types of both parts - text/plain and text/html.
    part1 = MIMEText(text, 'plain')
    part2 = MIMEText(rainHtmlTable, 'html')

    # Attach parts into message container.
    # According to RFC 2046, the last part of a multipart message, in this case
    # the HTML message, is best and preferred.
    msg.attach(part1)
    msg.attach(part2)

    # Send the message via local SMTP server.
    server = smtplib.SMTP('smtp.hispeed.ch')
    server.ehlo()
    server.starttls()
    server.login('user@address', 'passw')

    # sendmail function takes 3 arguments: sender's address, recipient's address
    # and message to send - here it is sent as one string.
    server.sendmail(me, you, msg.as_string())
    server.quit()
#-------------------
def getLatestWeatherRecord():
    sqlLatestRecord = """ SELECT
        "WeatherServiceInternet"."timeStampWeatherService",
        "WeatherServiceInternet"."RainIntegralSmall",
        "WeatherServiceInternet"."RainIntegralMedium",
        "WeatherServiceInternet"."RainIntegralLarge",
        "WeatherServiceInternet"."RainIntegralSmall_Max",
        "WeatherServiceInternet"."RainIntegralMedium_Max",
        "WeatherServiceInternet"."RainIntegralLarge_Max"
      FROM
        public."WeatherServiceInternet"
      WHERE "timeStampWeatherService" = (SELECT MAX("timeStampWeatherService") FROM public."WeatherServiceInternet")
        ;"""
    cur, conn = connectIpsDatabase()
    try:
        cur.execute(sqlLatestRecord)
        conn.commit()
        rows = np.asarray(cur.fetchall())

    except:
        exceptionSql = str(sys.exc_info()[1])[:46]
        print (sys.exc_info())
        #if exceptionSql[:-45] is "duplicate key value violates unique constrain":
        if (exceptionSql == "duplicate key value violates unique constraint"):
            print(ipBikeFile + " is already filed")
    return(rows)




#--------------------------------------
# MAIN ROUTINE
# loops until it finds the most recent GIF file and assigns to CurrentGif

weatherPath = "C:/Users/Adriano/Favorites/Documents/IPS/images/" # gifs are stored here
weatherUrl = "http://data.meteomedia.de//data/layers/zuerich1/zuerich1_radarfcst_" # website of weather service

timeNow = int(datetime.datetime.now().strftime("%Y%m%d%H%M"))
fiveMinRoundedTime = timeNow//5*5
print("fiveMinRoundedTime: " + str(fiveMinRoundedTime))

gifNonExisting = True;
offsetTime = fiveMinRoundedTime
counter =0
while (counter<20 and gifNonExisting == True):
    counter +=1
    weatherGif = weatherUrl+ str(fiveMinRoundedTime) + ".gif"
    try:
        with urllib.request.urlopen(weatherGif) as url:
            fd = url.read()
        p = ImageFile.Parser()
        p.feed(fd)
        rainGif = p.close()
        gifNonExisting = False
        print("file found: ", weatherGif, rainGif.format, rainGif.size, rainGif.mode)

    except:
        print ("Offset ", offsetTime, "not successful")
        offsetTime -= 5
        rainGif = Image.open(weatherPath + "CurrentPrecipitationsOriginal.gif") #this is just a fallback for testing.

#-------------------------------------
# analyze radar image
# creates 3 concentric areas which will be tested for rain. Any positive outcomes will be written to variables

cur, conn = connectIpsDatabase()
rainResults = [] # this array will be filled with variableName, RainIntegral, and RainMax

npGif = np.asarray(rainGif)
print ("npGif.shape", npGif.shape)
rainPositive = False
for homeRadius in rainDataArray:
    rainHome = rainGif.crop(((homeX-homeRadius[0]),(homeY-homeRadius[0]),(homeX+homeRadius[0]),(homeY+homeRadius[0])))
    rainHomeBW = rainHome.convert('L') #make it a greyscale
    npHomeRain = np.asarray(rainHomeBW)

    homeRadius.append([np.average(npHomeRain),np.std(npHomeRain),np.max(npHomeRain)])
    messagebody = ("npHomeRain["+str(homeRadius[0])+"]:"+ str(npHomeRain.shape) +", "
           + (" "*(10-len(str(homeRadius[0])+ str(npHomeRain.shape))))
           +  "avg "+ u'\u00b1' + " stdev: ",
           str(int(round(np.average(npHomeRain))))+ " "
           + u'\u00b1' + " " + str(round(np.std(npHomeRain),2))
           + " max: " +str(round(np.max(npHomeRain),2)))
    if (np.sum(npHomeRain)>0):
        rainPositive = True
    #set IPS variables
    IpsRpc("SetValueFloat", (homeRadius[1], np.average(npHomeRain)))
    idx = (npHomeRain>0)
    #print (npHomeRain[idx])

print("rainDataArray:", rainDataArray) # this array: radius, IpsVariable, postgresColumnName,[avg,stddev,max]
# insert into postgres
appendIntoPostgres(rainDataArray)


#--------------------------------------------------
# Draw concentric boxes
rainDraw = ImageDraw.Draw(rainGif)
for homeRadius in rainDataArray:
    rainDraw.rectangle([(homeX-homeRadius[0]),(homeY-homeRadius[0]),(homeX+homeRadius[0]),(homeY+homeRadius[0])],
        fill=None, outline=255)

# annotate with current datetime
textCoordinates= (10,400)
timeString = str(offsetTime)[7:8] + "." \
            + str(offsetTime)[4:6] + "." \
            + str(offsetTime)[:4] + ", " \
            + str(offsetTime)[8:10] + ":" \
            + str(offsetTime)[10:12]

arialBold = ImageFont.truetype(font=None, size=30, index=0, encoding='', filename="Arial.ttf")
rainDraw.text(textCoordinates, timeString , fill=255, font=arialBold, anchor=None)
rainDraw.text((textCoordinates[0]-1, textCoordinates[1]-1), timeString , fill=120, font=arialBold, anchor=None)

sender = "InternetWeatherService@whatever.com"
receivers = ["InsertRegisteredUserHere"]
subject = "Precipitation (rain/snow) imminent!"
attachment = rainGif


print(getLatestWeatherRecord()[0][6])

if ((rainPositive==True) & ((getLatestWeatherRecord()[0][6])>0)):
    sendEmail(sender, receivers, subject, messagebody, attachment, rainDataArray)
    print ("rain in sight")
else:
    print ("no rain in sight")



#rainGif.show()


und hier die Postgres-Tabellendefinition, der vollständigkeit halber:


CREATE TABLE "WeatherServiceInternet"
(
  "timeStampWeatherService" timestamp with time zone NOT NULL DEFAULT now(),
  "RainIntegralSmall" numeric,
  "RainIntegralMedium" numeric,
  "RainIntegralLarge" numeric,
  "RainIntegralSmall_Max" numeric,
  "RainIntegralMedium_Max" numeric,
  "RainIntegralLarge_Max" numeric,
  CONSTRAINT "timestamp" PRIMARY KEY ("timeStampWeatherService")
)
WITH (
  OIDS=FALSE
);
ALTER TABLE "WeatherServiceInternet"
  OWNER TO ips;