|
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
|
|
|
|