db.coastdat.fetch_raw_data()   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 37
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 22
nop 3
dl 0
loc 37
rs 9.352
c 0
b 0
f 0
1
#!/usr/bin/python3
2
# -*- coding: utf-8 -*-
3
4
from datetime import datetime
5
import logging
6
7
from pytz import timezone
8
from shapely.wkt import loads as wkt_loads
9
import numpy as np
10
import pandas as pd
11
12
import feedinlib.weather as weather
13
14
from . import tools
15
16
17
def get_weather(conn, geometry, year):
18
    r"""
19
    Get the weather data for the given geometry and create an oemof
20
    weather object.
21
    """
22
    rename_dc = {
23
        'ASWDIFD_S': 'dhi',
24
        'ASWDIR_S': 'dirhi',
25
        'PS': 'pressure',
26
        'T_2M': 'temp_air',
27
        'WSS_10M': 'v_wind',
28
        'Z0': 'z0'}
29
30
    if geometry.geom_type in ['Polygon', 'MultiPolygon']:
31
        # Create MultiWeather
32
        # If polygon covers only one data set switch to SingleWeather
33
        sql_part = """
34
            SELECT sp.gid, ST_AsText(point.geom), ST_AsText(sp.geom)
35
            FROM coastdat.cosmoclmgrid AS sp
36
            JOIN coastdat.spatial AS point ON (sp.gid=point.gid)
37
            WHERE st_intersects(ST_GeomFromText('{wkt}',4326), sp.geom)
38
            """.format(wkt=geometry.wkt)
39
        df = fetch_raw_data(sql_weather_string(year, sql_part), conn, geometry)
40
        obj = create_multi_weather(df, rename_dc)
41
    elif geometry.geom_type == 'Point':
42
        # Create SingleWeather
43
        sql_part = """
44
            SELECT sp.gid, ST_AsText(point.geom), ST_AsText(sp.geom)
45
            FROM coastdat.cosmoclmgrid AS sp
46
            JOIN coastdat.spatial AS point ON (sp.gid=point.gid)
47
            WHERE st_contains(sp.geom, ST_GeomFromText('{wkt}',4326))
48
            """.format(wkt=geometry.wkt)
49
        df = fetch_raw_data(sql_weather_string(year, sql_part), conn, geometry)
50
        obj = create_single_weather(df, rename_dc)
51
    else:
52
        logging.error('Unknown geometry type: {0}'.format(geometry.geom_type))
53
        obj = None
54
    return obj
55
56
57
def sql_weather_string(year, sql_part):
58
    """
59
    Creates an sql-string to read all datasets within a given geometry.
60
    """
61
    # TODO@Günni. Replace sql-String with alchemy/GeoAlchemy
62
    # Create string parts for where conditions
63
64
    return '''
65
    SELECT tsptyti.*, y.leap
66
    FROM coastdat.year as y
67
    INNER JOIN (
68
        SELECT tsptyd.*, sc.time_id
69
        FROM coastdat.scheduled as sc
70
        INNER JOIN (
71
            SELECT tspty.*, dt.name, dt.height
72
            FROM coastdat.datatype as dt
73
            INNER JOIN (
74
                SELECT tsp.*, typ.type_id
75
                FROM coastdat.typified as typ
76
                INNER JOIN (
77
                    SELECT spl.*, t.tsarray, t.id
78
                    FROM coastdat.timeseries as t
79
                    INNER JOIN (
80
                        SELECT sps.*, l.data_id
81
                        FROM (
82
                            {sql_part}
83
                            ) as sps
84
                        INNER JOIN coastdat.located as l
85
                        ON (sps.gid = l.spatial_id)) as spl
86
                    ON (spl.data_id = t.id)) as tsp
87
                ON (tsp.id = typ.data_id)) as tspty
88
            ON (tspty.type_id = dt.id)) as tsptyd
89
        ON (tsptyd.id = sc.data_id))as tsptyti
90
    ON (tsptyti.time_id = y.year)
91
    where y.year = '{year}'
92
    ;'''.format(year=year, sql_part=sql_part)
93
94
95
def fetch_raw_data(sql, connection, geometry):
96
    """
97
    Fetch the coastdat2 from the database, adapt it to the local time zone
98
    and create a time index.
99
    """
100
    tmp_dc = {}
101
    weather_df = pd.DataFrame(
102
        connection.execute(sql).fetchall(), columns=[
103
            'gid', 'geom_point', 'geom_polygon', 'data_id', 'time_series',
104
            'dat_id', 'type_id', 'type', 'height', 'year', 'leap_year']).drop(
105
        'dat_id', 1)
106
107
    # Get the timezone of the geometry
108
    tz = tools.tz_from_geom(connection, geometry)
109
110
    for ix in weather_df.index:
111
        # Convert the point of the weather location to a shapely object
112
        weather_df.loc[ix, 'geom_point'] = wkt_loads(
113
            weather_df['geom_point'][ix])
114
115
        # Roll the dataset forward according to the timezone, because the
116
        # dataset is based on utc (Berlin +1, Kiev +2, London +0)
117
        utc = timezone('utc')
118
        offset = int(utc.localize(datetime(2002, 1, 1)).astimezone(
119
            timezone(tz)).strftime("%z")[:-2])
120
121
        # Get the year and the length of the data array
122
        db_year = weather_df.loc[ix, 'year']
123
        db_len = len(weather_df['time_series'][ix])
124
125
        # Set absolute time index for the data sets to avoid errors.
126
        tmp_dc[ix] = pd.Series(
127
            np.roll(np.array(weather_df['time_series'][ix]), offset),
128
            index=pd.date_range(pd.datetime(db_year, 1, 1, 0),
129
                                periods=db_len, freq='H', tz=tz))
130
    weather_df['time_series'] = pd.Series(tmp_dc)
131
    return weather_df
132
133
134
def create_single_weather(df, rename_dc):
135
    """Create an oemof weather object for the given geometry"""
136
    my_weather = weather.FeedinWeather()
137
    data_height = {}
138
    name = None
139
    # Create a pandas.DataFrame with the time series of the weather data set
140
    weather_df = pd.DataFrame(index=df.time_series.iloc[0].index)
141
    for row in df.iterrows():
142
        key = rename_dc[row[1].type]
143
        weather_df[key] = row[1].time_series
144
        data_height[key] = row[1].height if not np.isnan(row[1].height) else 0
145
        name = row[1].gid
146
    my_weather.data = weather_df
147
    my_weather.timezone = weather_df.index.tz
148
    my_weather.longitude = df.geom_point.iloc[0].x
149
    my_weather.latitude = df.geom_point.iloc[0].y
150
    my_weather.geometry = df.geom_point.iloc[0]
151
    my_weather.data_height = data_height
152
    my_weather.name = name
153
    return my_weather
154
155
156
def create_multi_weather(df, rename_dc):
157
    """Create a list of oemof weather objects if the given geometry is a polygon
158
    """
159
    weather_list = []
160
    # Create a pandas.DataFrame with the time series of the weather data set
161
    # for each data set and append them to a list.
162
    for gid in df.gid.unique():
163
        gid_df = df[df.gid == gid]
164
        obj = create_single_weather(gid_df, rename_dc)
165
        weather_list.append(obj)
166
    return weather_list
167