diff --git a/examples/energy_market_outlooks/energy_market_plots.py b/examples/energy_market_outlooks/energy_market_plots.py new file mode 100644 index 0000000..d8442a2 --- /dev/null +++ b/examples/energy_market_outlooks/energy_market_plots.py @@ -0,0 +1,503 @@ +""" +In this script a user can specify several regions for which weather data will then be plotted. It was conceived as a +regular energy market update with temperature anomaly (indicative of heating demand) and 90m wind speed (indicative of +onshore wind generation) as the time-series variables. Two sets of ouput plots are produced: +- time-series data for each region and each variable +- an animation of temperture and pressure in a given region and map projection (frames also produced) +The variables used in the time-series may be straightforwardly changed; the variables shown in the animation are +not configurable (but may be changed in the function) since, in general, a different choice of variable will imply +a different optimal plot type. + +To run the script, an API account is required. Credentials should be updated in the script. A free trial can be +initiated here: https://www.meteomatics.com/en/api-trial-account-form/ + +I've experimented with a few different configurations but the code has not been robustly tested: let me know if you +try something that doesn't work and I'll think about an update. +""" + +import shapely.geometry as sgeometry +import matplotlib.pyplot as plt +import mm_python_module.api_connector.meteomatics.api as api +import cartopy.crs as ccrs +import geopandas as gpd +from PIL import Image +import datetime as dt +import pandas as pd +import xarray as xr +import numpy as np +import functools +import glob +import time +import os + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CONFIGURATION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# 1. What countries/regions do you want in your time-series? +# These are provided in two arguments. 'STANDARD_COUNTRIES' is a list of strings which are the names of polygons in +# geopandas' "naturalearth_lowres" built-in dataset. 'MANUAL_GEOMETRIES' is a dictionary whose keys are the names you +# want to give to your user-created polygons, and whose keys are the paths to geojson files containing those polygons. +# I advise that, if you make changes to this, you check that they have been interpreted correctly by switching +# 'SHOW_POLYGONS' to True. This will show them in your chosen map projection. Speaking of which... +STANDARD_COUNTRIES = ['United Kingdom', 'Germany', 'Netherlands', 'Italy'] +MANUAL_GEOMETRIES = {'Northern France': 'polygons/northern_france.geojson'} +SHOW_POLYGONS = False + +# 2. What map projection do you want to show your animated data in? +# Change entries in the dictionaries to change the bounding box for your animations. This will make a rectangular plot +# regardless of the projection. The dictionary values are then used to define the map projection. You can choose from +# any cartopy projection, but be aware that a) some projections take different arguments to define the transformations +# and b) some projections will not fill the rectangle defined by the dictionaries in certain parts of the globe (for +# instance, if you use AzimuthalEquidistant in Europe you'll see the fetched data get squeezed at the top of the plot). +TOP_LEFT = { + 'lat': 66.211199, + 'lon': -32.364446 +} +BOTTOM_RIGHT = { + 'lat': 35.526388, + 'lon': 32 +} +# CRS = ccrs.AzimuthalEquidistant(central_longitude=(TOP_LEFT['lon']+BOTTOM_RIGHT['lon'])/2, +# central_latitude=(TOP_LEFT['lat']+BOTTOM_RIGHT['lat'])/2) +CRS = ccrs.Mercator(central_longitude=(TOP_LEFT['lon']+BOTTOM_RIGHT['lon'])/2, + min_latitude=BOTTOM_RIGHT['lat'], + max_latitude=TOP_LEFT['lat']) + +# 3. What variables are you interested in? +# Currently this choice only affects time-series plots: the variables used in the animation are static (temperature and +# pressure, see animate <== make_nc). Keys should be the names you want on your axis labels, and should feature exactly +# one set of parentheses enclosing the units; values are the names of the corresponding meteomatics strings. +TIMESERIES_VARS = { + 'Temperature (C)': 't_2m:C', + '90m Wind Speed (m/s)': 'wind_speed_90m:ms', + 'Global Radiation (W/m2)': 'global_rad:W' +} + +# 4. How do you want to define the baseline (climatology) for your time-series? +# 15 years is apparently standard for energy industry; 30 years is standard in academic texts. Climatologies are all +# interpolated to 1hrly resolution, but I recommend sub-6hrly for data acquisition, since 0600 and 1800 vary between +# being daytime- and nighttime values throughout the year. Also note that the API assumes all times are UTC, so a +# smaller time-step is better for translating to other time-zones). Don't let your climatology get too out of date! +CLIMATOLOGY_START_YEAR = 2005 +CLIMATOLOGY_STOP_YEAR = 2020 +CLIMATOLOGY_STEP = dt.timedelta(hours=3) + +# IMPORTANT! Climatologies are obtained once per new run environment and then imported from .csv in order to reduce +# runtime. The script is not clever enough to know whether you have changed TIMESERIES_VARS or the period to be covered +# by climatology, so make sure you force the recalculation of climatology whenever changes are made. +REBUILD_CLIMATOLOGY = False + +# 5. Plot formatting. +# What period do you want your output (time-series and animations) to cover? +# What temporal resolution do you want for them both (set separately)? +# What spatial resolution do you want in your +# Note that the animation may fail if you set the time-step very high +LEAD_TIME_DAYS = 28 +START_TIME = dt.datetime.combine(dt.datetime.now().date(), dt.time(12)) # starts at 12pm on the day called +T_SERIES_LEAD = dt.timedelta(days=LEAD_TIME_DAYS) +T_SERIES_STEP = dt.timedelta(hours=1) +ANIMATION_STEP = dt.timedelta(hours=6) + +# API access +USERNAME = 'energy_market_plots' +PASSWORD = 'GkQ9qqq868Lm' + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ END CONFIGURATION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +def timer(func): + """Print the runtime of the decorated function""" + @functools.wraps(func) + def wrapper_timer(*args, **kwargs): + t_start_time = time.perf_counter() # 1 + value = func(*args, **kwargs) + end_time = time.perf_counter() # 2 + run_time = end_time - t_start_time # 3 + print(f"Finished {func.__name__!r} in {run_time:.4f} secs") + return value + return wrapper_timer + + +def get_geometries(standard_countries, manual_geometries, show=False): + """ + Uses geopandas' built-in countries for those polygons which correspond to a whole country; otherwise can use custom + geometries provided in GeoJSON format (with corresponding user-defined polygon names: see parameter docstrings). + :param standard_countries: list of country names as they appear in naturalearth_lowres + :param manual_geometries: dictionary of: + polygon names : GeoJSON paths + :param show: Bool, if True will illustrate location and shape of polygons on given map projection + :return: dictionary of: + polygon names (standard_countries + manual_geometries.keys) : shapely.[Multi]Polygon + """ + countries = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres')) + geometries = {} + geometries_projected = {} # will be filled with geometries in chosen projection if show is True + for country in standard_countries: + geometries[country] = countries[countries.name == country].geometry.values[0] + if show: + geometries_projected[country] = countries[countries.name == country].to_crs(CRS.proj4_init).geometry + for country in manual_geometries: + geometries[country] = gpd.read_file(manual_geometries[country]).geometry.values[0] + if show: + geometries_projected[country] = gpd.read_file(manual_geometries[country]).to_crs(CRS.proj4_init).geometry + if show: + show_geometries(geometries_projected) + return geometries + + +def show_geometries(geometries): # TODO could add an option to show overlap + """ + Plots the projected polygons + :param geometries: shapely.[Multi]Polygon objects corresponding to polygons projected into CRS (defined in header) + """ + fig = plt.figure() + ax = fig.add_subplot(projection=CRS) + ax.stock_img() + ax.set_extent([TOP_LEFT['lon'], BOTTOM_RIGHT['lon'], TOP_LEFT['lat'], BOTTOM_RIGHT['lat']]) + for geom in geometries: + geometries[geom].plot(ax=ax) + plt.show() + + +@timer +def get_polygon_climatology(geometries, location): # TODO could save climatology locally and only fetch if required + if os.path.exists(os.path.join('climatologies', '{}.csv'.format(location))) and not REBUILD_CLIMATOLOGY: + print('Climatology for {} exists: reading climatology'.format(location)) + else: + print('New climatology required for {}.'.format(location)) + os.makedirs('climatologies', exist_ok=True) + write_climatology(geometries, location) + df = pd.read_csv(os.path.join('climatologies', '{}.csv'.format(location)), index_col=[0, 1]) + return df + + +def write_climatology(geometries, location): + """ + This only looks hefty because of the if/else block, plus the fact that I've extended API calls over multiple lines. + Makes a climatology dataframe by joining years together sequentially. + :param geometries: dictionary of: + locations : shapely.[Multi]Polygons + :param location: the name of the location. passed because desired for name of written .csv + """ + geometry = geometries[location] + tuple_lists = make_tuple_lists(geometry) + print('Fetching climatology for {}.'.format(location)) + # had to break this down because of 300s timeout. 3hrly still seems reasonable and a bit faster; could go 1hrly now + for year in range(CLIMATOLOGY_START_YEAR, CLIMATOLOGY_STOP_YEAR): + if year == CLIMATOLOGY_START_YEAR: + df = api.query_polygon( + latlon_tuple_lists=tuple_lists, + startdate=dt.datetime(year, 1, 1), + enddate=dt.datetime(year, 12, 31, int(24 - CLIMATOLOGY_STEP.seconds/3600.)), + interval=CLIMATOLOGY_STEP, + parameters=TIMESERIES_VARS.values(), + aggregation=['mean'], + username=USERNAME, + password=PASSWORD, + operator='U', # in case of MultiPolygons + model='ecmwf-era5', + polygon_sampling='adaptive_grid' + ) + else: + df = df.append( + api.query_polygon( + latlon_tuple_lists=tuple_lists, + startdate=dt.datetime(year, 1, 1), + enddate=dt.datetime(year, 12, 31, int(24 - CLIMATOLOGY_STEP.seconds/3600.)), + interval=CLIMATOLOGY_STEP, + parameters=TIMESERIES_VARS.values(), + aggregation=['mean'], + username=USERNAME, + password=PASSWORD, + operator='U', # in case of MultiPolygons + model='ecmwf-era5', + polygon_sampling='adaptive_grid' + ) + ) + # Query returns MultiIndexed DataFrame, but outer index is polygon1 even if geometry is a MultiPolygon (because + # of the union operation). To make this easier to work with, I cross-section to get rid of the outer index level. + # To future-proof, make sure that there is only one outer index - if so, remove it. + assert len(df.index.get_level_values(0).unique()) == 1 + df = df.xs(key='polygon1') + df = df.resample('H').interpolate(method='cubic') + climatology = df.groupby([df.index.dayofyear, df.index.time]).mean() + climatology.index.names = ['doy', 'time'] + climatology.to_csv(os.path.join('climatologies', '{}.csv'.format(location))) + + +@timer +def get_polygon_data(geometry): # TODO could make parameters variable + """ + Gets mean weather data for 2m temperature and 90m wind-speed (currently static) within polygon defined by geometry + :param geometry: shapely.[Multi]Polygon + :return: pandas.DataFrame of spatial mean time-series data for the polygon; time-series parameters defined in header + """ + tuple_lists = make_tuple_lists(geometry) + print('Fetching time-series data for the next {} days.'.format(LEAD_TIME_DAYS)) + df = api.query_polygon( + latlon_tuple_lists=tuple_lists, + startdate=START_TIME, + enddate=START_TIME + T_SERIES_LEAD, + interval=T_SERIES_STEP, + parameters=TIMESERIES_VARS.values(), + aggregation=['mean'], + username=USERNAME, + password=PASSWORD, + operator='U', # not necessary for the single polygon, but doesn't break and is necessary for multi-polygons + model='ecmwf-vareps', + polygon_sampling='adaptive_grid' + ) + # Query returns MultiIndexed DataFrame, but outer index is polygon1 even if geometry is a MultiPolygon (because + # of the union operation). To make this easier to work with, I cross-section to get rid of the outer index level. + # To future-proof, make sure that there is only one outer index - if so, remove it. + assert len(df.index.get_level_values(0).unique()) == 1 + df = df.xs(key='polygon1') + return df + + +def make_tuple_lists(geometry): + """ + Prepare the tuple list argument for API query from polygon data + :param geometry: shapely.[Multi]Polygon + :return: tuple lists (tuple list for each Polygon) + """ + retval = [] + if type(geometry) is sgeometry.multipolygon.MultiPolygon: + for polygon in geometry: # this is the correct way to access Polygons within a MultiPolygon... + retval.append(make_tuple_list(polygon)) + elif type(geometry) is sgeometry.polygon.Polygon: + retval.append(make_tuple_list(geometry)) # ...but you can't loop over a single Polygon (which is dumb) + else: + # code should be written such that only [Multi]Polygons make it here; if imported modules change, will raise + raise TypeError('geometries should contain only polygons or multipolygons') + return retval + + +def make_tuple_list(polygon): + """ + Subroutine of make_tuple_lists. Argument will always be a Polygon, since parent function loops over MultiPolygon + :param polygon: shapely.Polygon + :return: tuple list + """ + tuple_list = [] + lons, lats = polygon.exterior.coords.xy + for i in range(len(lons)): + tuple_list.append((lats[i], lons[i])) + return tuple_list + + +def get_combined_data(geometries, key, mins, maxs): + """ + Reads the climatology and forecast data, combines them, writes the result to file and updates the min/max dicts. + :param geometries: dict of shapely.[Multi]Polygons + :param key: the country/region of interest + :param mins: dict of minimum values for variables across all regions + :param maxs: dict of maximum values for variables across all regions + :return: (updated) dicts of minimum/maximum values for variables across all regions + """ + clim = get_polygon_climatology(geometries, key) # pass the key in order to name/lookup the file + fcst = get_polygon_data(geometries[key]) + combined = combine(fcst, clim) + for var in fcst.columns: + mins[var] = min(mins[var], min(fcst[var])) + maxs[var] = max(maxs[var], max(fcst[var])) + os.makedirs('time-series', exist_ok=True) + combined.to_csv(os.path.join('time-series', 'time-series data {}.csv'.format(key))) + return mins, maxs + + +def combine(fcst, clim): + """ + Joins the forecast and the corresponding climatology data together into one DataFrame. + :param fcst: + :param clim: + :return: + """ + relevant_clim = pd.DataFrame(index=fcst.index, columns=fcst.columns) + for i in range(len(fcst)): + tstamp = fcst.index[i] + relevant_clim.loc[tstamp] = clim.loc[(tstamp.dayofyear, tstamp.time().strftime('%H:%M:%S'))] + return pd.merge(fcst, relevant_clim, left_index=True, right_index=True, suffixes=('_forecast', '_climatology')) + + +def plot_timeseries(title, mins, maxs): + """ + Generic function for plotting time-series data and anomaly from climatology of any variable (specified in header) + :param title: the name of the region being plotted: will be the title of the plot and the name of the file + :param mins: dictionary of global minimum values corresponding to each variable + :param maxs: dictionary of global maximum values corresponding to each variable + """ + df = pd.read_csv(os.path.join('time-series', 'time-series data {}.csv'.format(title)), index_col=0, parse_dates=True) + for var in TIMESERIES_VARS: + try: + assert '(' in var + except AssertionError: + raise ValueError('Make sure the TIMESERIES_VARS dict is formatted correctly') + mm_string = TIMESERIES_VARS[var] + unit_index = len(var.split('(')[0]) + vmin = mins[mm_string] - (maxs[mm_string] - mins[mm_string]) * 0.1 + vmax = maxs[mm_string] + (maxs[mm_string] - mins[mm_string]) * 0.1 + f, (ax1, ax2) = plt.subplots(2, sharex='col', figsize=(12, 7)) + climatology_years = CLIMATOLOGY_STOP_YEAR - CLIMATOLOGY_START_YEAR + df['{}_climatology'.format(mm_string)].plot(label='{}-year mean'.format(climatology_years), ax=ax1, c='b') + df['{}_forecast'.format(mm_string)].plot(label='forecast', ax=ax1, ylim=[vmin, vmax], c='k') + pd.Series(np.zeros(len(df.index)), index=df.index).plot(ax=ax2, linestyle='dashed', c='b') + (df['{}_forecast'.format(mm_string)] - df['{}_climatology'.format(mm_string)]).plot(ax=ax2, c='k') + # plot formatting options + f.suptitle('{}'.format(title)) + ax1.set_ylabel(var) + ax1.legend() + ax2.set_ylabel(var[:unit_index]+'Anomaly '+var[unit_index:]) + ax1.grid(True) + ax2.grid(True) + f.tight_layout() + os.makedirs(os.path.join('time-series', '{}'.format(var.replace('/', ''))), exist_ok=True) + plt.savefig(os.path.join('time-series', '{}'.format(var.replace('/', '')), '{}.png'.format(title))) + plt.close(f) + + +@ timer +def make_nc(): + """ + A single API call to retrieve all this data is too big, so I loop over days in our forecast range and stack the + grids together. Using query_grid_timeseries is still desirable for this, since it can be manipulated into an + xarray.Dataset very easily. + :return: xarray.Dataset containing 2m temperature and MSLP + """ + for day in range(LEAD_TIME_DAYS): + query_start = START_TIME + dt.timedelta(days=day) + df = api.query_grid_timeseries( + startdate=query_start, + enddate=query_start + dt.timedelta(hours=23), # ensures we don't double-count days + interval=ANIMATION_STEP, + parameters=['t_2m:C', 'msl_pressure:hPa'], + lat_N=np.ceil(TOP_LEFT['lat'] + 1), + lon_W=np.floor(TOP_LEFT['lon'] - 1), + lat_S=np.floor(BOTTOM_RIGHT['lat'] - 1), + lon_E=np.ceil(BOTTOM_RIGHT['lon'] + 1), + res_lon=0.5, + res_lat=0.5, + username=USERNAME, + password=PASSWORD, + model='ecmwf-vareps' + ) + if day == 0: + nc = df.to_xarray() + else: + tmp = df.to_xarray() + nc = xr.concat([nc, tmp], dim='validdate') + try: + # TODO this currently fails because Meteomatics data is float64 and to_netcdf wants float32 + nc.to_netcdf('run_{}'.format(dt.datetime.now().strftime('%Y-%m-%d_%Hh00.nc'))) + except ValueError: + pass + return nc + + +def transform_coords(nc): + """ + I found the suggested method of adding a transorm=CRS keyword argument to plotting functions not to work with + e.g. Azimuthal equidistant. Perhaps this is something to do with pcolor. Anyway, here's my solution: define a + 2D latitude and longitude variable which corresponds to the data variables; transform those and return them. + :param nc: xarray dataset containing latitude and longitude coordinates + :return: longitude and latitudes transformed to your chosen CRS + """ + # We first need latitude- and longitude arrays of equal size. Since we may be working with a map which has different + # x- and y-dimensions, and also since we may not be mapping to a rectilinear coordinate system, we first have to + # make 2D arrays of each + meshed_lon, meshed_lat = np.meshgrid(nc.lon.values, nc.lat.values) + # We can then transform these to our target CRS. The source CRS is PlateCarree() i.e. PlateCarree with default args + # i.e. quadratic grid, as this is the coordinate system returned from the Meteomatics API. + transformed_output = CRS.transform_points(ccrs.PlateCarree(), meshed_lon, meshed_lat) + # This gives us a 3D array of x, y, z; the latter will, for our purposes, be identically 0. We can subset this as + transformed_lons = transformed_output[:, :, 0] + transformed_lats = transformed_output[:, :, 1] + # Note that these are again 2D (because the same latitudes are not used for all longitudes and vice versa in a + # non-rectangular projection) and hence cannot be used as coordinates in an xarray.Dataset, but can be added as + # additional data variables if you like. + # This produces lat/lon fields for each element of the weather variables which allow them to be plotted in this + # projection. The process needn't be repeated for each time-step since we assume that the spatial domain is static. + return transformed_lons, transformed_lats + + +def make_frames(nc, lons, lats, animation_path): + """ + Make all the individual images which will comprise the final gif. + :param nc: the netCDF containing the data + :param lons: the 2D grid of longitudes transformed to the CRS != nc.lon.values + :param lats: the 2D grid of latitudes transformed to the CRS != nc.lat.values + :param animation_path: location in which to save the frames + :return: + """ + # one advantage of having built a Dataset of all the values we're going to animate is that I can now access + # the global min and max of all the data for our colorbar/contour levels + mslp_min = nc['msl_pressure:hPa'].min() + mslp_max = nc['msl_pressure:hPa'].max() + contour_levels = np.arange(np.floor(mslp_min), np.ceil(mslp_max), 2) + t2m_min = nc['t_2m:C'].min() + t2m_max = nc['t_2m:C'].max() + + os.makedirs(animation_path, exist_ok=True) + for fle in glob.glob(os.path.join(animation_path, '*')): + os.remove(fle) # remove all the previous animation bits and pieces + + # TODO various bits of formatting can be done + # this will save a bunch of static images, which can of course be giffed + for t_step in range(len(nc.validdate)): + fig = plt.figure() + ax = fig.add_subplot(projection=CRS) + ax.set_extent([TOP_LEFT['lon'], BOTTOM_RIGHT['lon'], TOP_LEFT['lat'], BOTTOM_RIGHT['lat']]) + nc_step = nc.isel(validdate=t_step) + cbar_map = ax.pcolor(lons, lats, nc_step['t_2m:C'], vmin=t2m_min, vmax=t2m_max) + contours = ax.contour(lons, lats, nc_step['msl_pressure:hPa'], levels=contour_levels, colors='black') + ax.clabel(contours, contours.levels[::5]) + ax.coastlines() + plt.colorbar(cbar_map) + plt.title(pd.to_datetime(nc.validdate[t_step].values).strftime('%Y-%m-%d %Hh00')) + plt.savefig(os.path.join(animation_path, '{}_{}'.format( + str(t_step).zfill(4), # include this so that order is preserved even if we cross into January + pd.to_datetime(nc.validdate[t_step].values).strftime('%Y-%m-%d %Hh00.png') + ))) + plt.close(fig) + + +def animate(animation_path='animation'): + """ + Makes an animation of the temperature and pressure situation over the forecast time- and region specified. + All arguments other than the name of the animation directory are defined in the preamble. + :param animation_path: + :return: + """ + nc = make_nc() + lons, lats = transform_coords(nc) + make_frames(nc, lons, lats, animation_path) + + # I'd have liked this to be a FuncAnimation with a slider for time control, but that seems complicated when + # including multiple functions on a single frame (as I do with pcolor and contour) so I cheat by making a GIF + # out of multiple images per https://bit.ly/3kRZOmB, and please check out my question https://bit.ly/3nwhNAT + # if you have suggestions on how to improve the readability of the GIF" + img, *imgs = [Image.open(f) for f in sorted(glob.glob(os.path.join(animation_path, '*.png')))] + # 'duration' (below) means duration of each frame in milliseconds + img.save(fp=os.path.join(animation_path, 'animation.gif'), format='GIF', append_images=imgs, save_all=True, + duration=200, loop=0) + + +if __name__ == '__main__': + # get the geometries of the polygons for which we want time-series plots + geometries = get_geometries( + standard_countries=STANDARD_COUNTRIES, + manual_geometries=MANUAL_GEOMETRIES, + show=SHOW_POLYGONS + ) + + # for each of those polygons, write the time-series data and get a global min/max for each variable + mins = {var: np.inf for var in TIMESERIES_VARS.values()} + maxs = {var: -np.inf for var in TIMESERIES_VARS.values()} + for key in geometries: + mins, maxs = get_combined_data(geometries, key, mins, maxs) + + # plot the time-series using these global values + for key in geometries: + plot_timeseries(key, mins, maxs) + + # now make the GIF + animate('animation') # all the other variables for this are controlled in the preamble diff --git a/examples/energy_market_outlooks/polygons/northern_france.csv b/examples/energy_market_outlooks/polygons/northern_france.csv new file mode 100644 index 0000000..c2d7687 --- /dev/null +++ b/examples/energy_market_outlooks/polygons/northern_france.csv @@ -0,0 +1,270 @@ +7.5142861, 47.6394850 +7.6311716, 47.8831970 +7.8125366, 48.1917256 +7.9389425, 48.4583519 +8.1477871, 48.7090879 +8.4099985, 49.0378679 +8.0309784, 49.1996535 +7.2564176, 49.4288400 +6.2401959, 49.5822260 +5.2075250, 49.8025412 +4.1576554, 49.9618223 +3.8176420, 50.0253876 +3.6695242, 50.2507175 +3.6008432, 50.4819783 +3.3014232, 50.4924633 +3.3205518, 50.6616494 +3.2355258, 50.7746817 +2.9305742, 50.7885745 +2.7409928, 50.8683781 +2.5156053, 51.0823905 +2.1705074, 51.0508911 +1.9383913, 51.0068423 +1.7413687, 50.9497758 +1.5438481, 50.8857087 +1.5216229, 50.4679945 +1.4339794, 50.1557859 +1.0876877, 49.9618223 +0.5766450, 49.8875565 +0.2640013, 49.7373503 +0.1867676, 49.7240353 +0.1101158, 49.6782925 +0.0558437, 49.5073767 +0.1027103, 49.4020374 +0.0013853, 49.3407831 +-0.1476240, 49.3094559 +-0.2863332, 49.3134848 +-0.4173401, 49.3591227 +-0.5306932, 49.3671720 +-0.7601279, 49.3680663 +-0.9435734, 49.4033778 +-1.0610485, 49.4091862 +-1.1290604, 49.4176740 +-1.1867674, 49.4569670 +-1.2540923, 49.5510527 +-1.2066901, 49.6013655 +-1.2142470, 49.6338409 +-1.2279867, 49.6645168 +-1.2485964, 49.6956177 +-1.2815719, 49.7071643 +-1.3619495, 49.7138246 +-1.4320224, 49.7111606 +-1.4670588, 49.7111606 +-1.5275138, 49.6716274 +-1.5673591, 49.6809583 +-1.7136877, 49.6880664 +-1.8112401, 49.6925085 +-1.8229189, 49.7107166 +-1.8490245, 49.7240353 +-1.8785650, 49.7164885 +-1.9136014, 49.7364628 +-1.9445159, 49.7333562 +-1.9678735, 49.7156005 +-1.9521479, 49.6680722 +-1.8779580, 49.6449576 +-1.8580253, 49.6138244 +-1.8669836, 49.5608522 +-1.8978007, 49.5274368 +-1.8435286, 49.3761142 +-1.7975003, 49.3479408 +-1.6295923, 49.1704813 +-1.6467671, 49.1075903 +-1.6069217, 48.9644414 +-1.6254704, 48.8317296 +-1.5869990, 48.8082201 +-1.5725723, 48.7199612 +-1.6131046, 48.6914137 +-1.6344013, 48.6419836 +-1.7200247, 48.6347237 +-1.7887236, 48.6329086 +-1.8107072, 48.6474278 +-1.7997154, 48.6837076 +-1.8436827, 48.7127126 +-1.9673406, 48.6982122 +-2.0607711, 48.6456131 +-2.1899249, 48.6401687 +-2.2613718, 48.6619428 +-2.3135829, 48.7018377 +-2.4592245, 48.6655710 +-2.5405513, 48.6310934 +-2.6641805, 48.5511601 +-2.7972229, 48.6691988 +-2.9126370, 48.7543780 +-2.9538563, 48.8104811 +-2.9730920, 48.8683282 +-3.1077418, 48.8918096 +-3.2313997, 48.8791671 +-3.4182607, 48.8357975 +-3.5089432, 48.8538727 +-3.6326011, 48.7978182 +-3.6243573, 48.7054629 +-3.8304539, 48.7380783 +-3.8551854, 48.7090879 +-3.9376241, 48.7054629 +-3.9486159, 48.7489453 +-4.0228107, 48.7688622 +-4.0813948, 48.6927734 +-4.1638184, 48.7018377 +-4.2434981, 48.6746401 +-4.3470693, 48.6818942 +-4.5971332, 48.6292781 +-4.7757502, 48.5438872 +-4.8010323, 48.4547089 +-4.7974738, 48.3525987 +-4.7672463, 48.3188208 +-4.6937385, 48.3261261 +-4.6745028, 48.3370819 +-4.5975601, 48.3252130 +-4.6236656, 48.2932443 +-4.6435883, 48.2566842 +-4.5810723, 48.2338209 +-4.5714545, 48.1720387 +-4.5350441, 48.1615055 +-4.4739021, 48.2292470 +-4.3179557, 48.1944720 +-4.2901491, 48.1308093 +-4.3110858, 48.0941330 +-4.3749758, 48.1138498 +-4.5831333, 48.0845012 +-4.6307400, 48.0702794 +-4.6669459, 48.0767026 +-4.6820597, 48.0633965 +-4.7205310, 48.0670675 +-4.7179337, 48.0473329 +-4.7474681, 48.0395287 +-4.5810723, 47.9954359 +-4.5542798, 47.9949764 +-4.5487839, 48.0046250 +-4.5037158, 47.9940574 +-4.4274782, 47.9582033 +-4.3856021, 47.9034548 +-4.3543661, 47.8311350 +-4.3698117, 47.8279082 +-4.3862891, 47.8196098 +-4.3849213, 47.8053149 +-4.3708538, 47.7919388 +-4.2606289, 47.7850188 +-4.1771230, 47.7960904 +-4.1624502, 47.8122323 +-4.1569512, 47.8339006 +-4.1640702, 47.8412750 +-4.1397885, 47.8564814 +-4.1226196, 47.8578636 +-4.1049892, 47.8583243 +-4.0656243, 47.8537170 +-4.0525814, 47.8477268 +-4.0409098, 47.8389707 +-4.0072652, 47.8454227 +-3.9674384, 47.8495700 +-3.9724004, 47.8790524 +-3.9591983, 47.8878018 +-3.9442339, 47.8790524 +-3.9049564, 47.8315959 +-3.8788605, 47.8154601 +-3.8521774, 47.7873255 +-3.7876004, 47.7822505 +-3.7285194, 47.7970129 +-3.6742473, 47.7720989 +-3.5292927, 47.7573294 +-3.4998373, 47.7226969 +-3.4502890, 47.6894281 +-3.3988953, 47.6945122 +-3.3748637, 47.6838812 +-3.3472407, 47.6852680 +-3.3137526, 47.6871169 +-3.2746124, 47.6741727 +-3.2279154, 47.6464244 +-3.2022861, 47.6186613 +-3.1679367, 47.6061630 +-3.1475789, 47.5797681 +-3.1433571, 47.5422366 +-3.1639668, 47.5274020 +-3.1502270, 47.4838009 +-3.1317950, 47.4689497 +-3.1022658, 47.4647720 +-3.0677883, 47.4670930 +-3.0912781, 47.4884410 +-3.1153247, 47.5158095 +-3.1194347, 47.5399190 +-3.1070709, 47.5570671 +-3.0590061, 47.5617008 +-3.0095714, 47.5607741 +-2.9491407, 47.5297202 +-2.8961654, 47.5301838 +-2.8583810, 47.5079251 +-2.8543651, 47.4944726 +-2.8207397, 47.4851930 +-2.7877272, 47.4800885 +-2.7768040, 47.4916889 +-2.7500142, 47.4940086 +-2.7273573, 47.5005034 +-2.7046980, 47.4986479 +-2.6798700, 47.4851930 +-2.6654432, 47.4921528 +-2.6523905, 47.5005034 +-2.6400247, 47.4977200 +-2.6043013, 47.4926168 +-2.5891875, 47.5097803 +-2.5452202, 47.5051421 +-2.5342284, 47.5102442 +-2.5108708, 47.4916889 +-2.4971310, 47.4666288 +-2.5101838, 47.4554872 +-2.4875132, 47.4355192 +-2.4662165, 47.4392347 +-2.4586597, 47.4331968 +-2.5076273, 47.4039264 +-2.5646474, 47.3792894 +-2.5649597, 47.3723146 +-2.5323459, 47.3546410 +-2.5113745, 47.3341694 +-2.5141225, 47.3081031 +-2.5539678, 47.3025158 +-2.5539678, 47.2857503 +-2.4179441, 47.2559320 +-2.3739768, 47.2699114 +-2.2956601, 47.2242315 +-2.2654326, 47.2335572 +-2.2283352, 47.2410165 +-2.1912378, 47.2326247 +-2.1678788, 47.2027764 +-2.1761227, 47.1794457 +-2.2056715, 47.1701106 +-2.2413949, 47.1542371 +-2.2413949, 47.1271478 +-2.1603302, 47.1168689 +-2.1122410, 47.0841503 +-2.0985012, 47.0701218 +-2.0916313, 47.0345666 +-2.1328507, 47.0177164 +-2.2056715, 47.0083528 +-2.2276551, 47.0205251 +-2.2647525, 47.0345666 +-2.3059718, 47.0280143 +-2.3087198, 46.9858736 +-2.2178588, 46.9324477 +-2.1520863, 46.8141588 +-1.9459335, 46.6748833 +-1.8182099, 46.4898835 +-1.6450887, 46.4085109 +-1.5145609, 46.3914640 +-1.4692196, 46.3308098 +-1.3719328, 46.3402914 +-1.3156245, 46.2786312 +-1.2134430, 46.2710375 +-0.7627784, 46.2862239 +-0.5154625, 46.2900199 +-0.0153347, 46.1075173 +0.5617357, 46.0503610 +0.8578423, 46.1493944 +1.2535477, 46.3620930 +1.8086346, 46.5588603 +2.3582255, 46.6720565 +3.6882355, 46.7436250 +4.7544419, 46.8714582 +5.3699838, 46.9089984 +5.5073815, 47.0663803 +5.8646156, 47.2344896 +6.4197024, 47.3499890 +7.2990479, 47.3871931 +7.5142861, 47.6394850 \ No newline at end of file diff --git a/examples/energy_market_outlooks/polygons/northern_france.geojson b/examples/energy_market_outlooks/polygons/northern_france.geojson new file mode 100644 index 0000000..be26b9a --- /dev/null +++ b/examples/energy_market_outlooks/polygons/northern_france.geojson @@ -0,0 +1,1087 @@ +{ "type" : "Polygon", + "coordinates": [ + [ + [ + 7.5142861, + 47.639485 + ], + [ + 7.6311716, + 47.883197 + ], + [ + 7.8125366, + 48.1917256 + ], + [ + 7.9389425, + 48.4583519 + ], + [ + 8.1477871, + 48.7090879 + ], + [ + 8.4099985, + 49.0378679 + ], + [ + 8.0309784, + 49.1996535 + ], + [ + 7.2564176, + 49.42884 + ], + [ + 6.2401959, + 49.582226 + ], + [ + 5.207525, + 49.8025412 + ], + [ + 4.1576554, + 49.9618223 + ], + [ + 3.817642, + 50.0253876 + ], + [ + 3.6695242, + 50.2507175 + ], + [ + 3.6008432, + 50.4819783 + ], + [ + 3.3014232, + 50.4924633 + ], + [ + 3.3205518, + 50.6616494 + ], + [ + 3.2355258, + 50.7746817 + ], + [ + 2.9305742, + 50.7885745 + ], + [ + 2.7409928, + 50.8683781 + ], + [ + 2.5156053, + 51.0823905 + ], + [ + 2.1705074, + 51.0508911 + ], + [ + 1.9383913, + 51.0068423 + ], + [ + 1.7413687, + 50.9497758 + ], + [ + 1.5438481, + 50.8857087 + ], + [ + 1.5216229, + 50.4679945 + ], + [ + 1.4339794, + 50.1557859 + ], + [ + 1.0876877, + 49.9618223 + ], + [ + 0.576645, + 49.8875565 + ], + [ + 0.2640013, + 49.7373503 + ], + [ + 0.1867676, + 49.7240353 + ], + [ + 0.1101158, + 49.6782925 + ], + [ + 0.0558437, + 49.5073767 + ], + [ + 0.1027103, + 49.4020374 + ], + [ + 0.0013853, + 49.3407831 + ], + [ + -0.147624, + 49.3094559 + ], + [ + -0.2863332, + 49.3134848 + ], + [ + -0.4173401, + 49.3591227 + ], + [ + -0.5306932, + 49.367172 + ], + [ + -0.7601279, + 49.3680663 + ], + [ + -0.9435734, + 49.4033778 + ], + [ + -1.0610485, + 49.4091862 + ], + [ + -1.1290604, + 49.417674 + ], + [ + -1.1867674, + 49.456967 + ], + [ + -1.2540923, + 49.5510527 + ], + [ + -1.2066901, + 49.6013655 + ], + [ + -1.214247, + 49.6338409 + ], + [ + -1.2279867, + 49.6645168 + ], + [ + -1.2485964, + 49.6956177 + ], + [ + -1.2815719, + 49.7071643 + ], + [ + -1.3619495, + 49.7138246 + ], + [ + -1.4320224, + 49.7111606 + ], + [ + -1.4670588, + 49.7111606 + ], + [ + -1.5275138, + 49.6716274 + ], + [ + -1.5673591, + 49.6809583 + ], + [ + -1.7136877, + 49.6880664 + ], + [ + -1.8112401, + 49.6925085 + ], + [ + -1.8229189, + 49.7107166 + ], + [ + -1.8490245, + 49.7240353 + ], + [ + -1.878565, + 49.7164885 + ], + [ + -1.9136014, + 49.7364628 + ], + [ + -1.9445159, + 49.7333562 + ], + [ + -1.9678735, + 49.7156005 + ], + [ + -1.9521479, + 49.6680722 + ], + [ + -1.877958, + 49.6449576 + ], + [ + -1.8580253, + 49.6138244 + ], + [ + -1.8669836, + 49.5608522 + ], + [ + -1.8978007, + 49.5274368 + ], + [ + -1.8435286, + 49.3761142 + ], + [ + -1.7975003, + 49.3479408 + ], + [ + -1.6295923, + 49.1704813 + ], + [ + -1.6467671, + 49.1075903 + ], + [ + -1.6069217, + 48.9644414 + ], + [ + -1.6254704, + 48.8317296 + ], + [ + -1.586999, + 48.8082201 + ], + [ + -1.5725723, + 48.7199612 + ], + [ + -1.6131046, + 48.6914137 + ], + [ + -1.6344013, + 48.6419836 + ], + [ + -1.7200247, + 48.6347237 + ], + [ + -1.7887236, + 48.6329086 + ], + [ + -1.8107072, + 48.6474278 + ], + [ + -1.7997154, + 48.6837076 + ], + [ + -1.8436827, + 48.7127126 + ], + [ + -1.9673406, + 48.6982122 + ], + [ + -2.0607711, + 48.6456131 + ], + [ + -2.1899249, + 48.6401687 + ], + [ + -2.2613718, + 48.6619428 + ], + [ + -2.3135829, + 48.7018377 + ], + [ + -2.4592245, + 48.665571 + ], + [ + -2.5405513, + 48.6310934 + ], + [ + -2.6641805, + 48.5511601 + ], + [ + -2.7972229, + 48.6691988 + ], + [ + -2.912637, + 48.754378 + ], + [ + -2.9538563, + 48.8104811 + ], + [ + -2.973092, + 48.8683282 + ], + [ + -3.1077418, + 48.8918096 + ], + [ + -3.2313997, + 48.8791671 + ], + [ + -3.4182607, + 48.8357975 + ], + [ + -3.5089432, + 48.8538727 + ], + [ + -3.6326011, + 48.7978182 + ], + [ + -3.6243573, + 48.7054629 + ], + [ + -3.8304539, + 48.7380783 + ], + [ + -3.8551854, + 48.7090879 + ], + [ + -3.9376241, + 48.7054629 + ], + [ + -3.9486159, + 48.7489453 + ], + [ + -4.0228107, + 48.7688622 + ], + [ + -4.0813948, + 48.6927734 + ], + [ + -4.1638184, + 48.7018377 + ], + [ + -4.2434981, + 48.6746401 + ], + [ + -4.3470693, + 48.6818942 + ], + [ + -4.5971332, + 48.6292781 + ], + [ + -4.7757502, + 48.5438872 + ], + [ + -4.8010323, + 48.4547089 + ], + [ + -4.7974738, + 48.3525987 + ], + [ + -4.7672463, + 48.3188208 + ], + [ + -4.6937385, + 48.3261261 + ], + [ + -4.6745028, + 48.3370819 + ], + [ + -4.5975601, + 48.325213 + ], + [ + -4.6236656, + 48.2932443 + ], + [ + -4.6435883, + 48.2566842 + ], + [ + -4.5810723, + 48.2338209 + ], + [ + -4.5714545, + 48.1720387 + ], + [ + -4.5350441, + 48.1615055 + ], + [ + -4.4739021, + 48.229247 + ], + [ + -4.3179557, + 48.194472 + ], + [ + -4.2901491, + 48.1308093 + ], + [ + -4.3110858, + 48.094133 + ], + [ + -4.3749758, + 48.1138498 + ], + [ + -4.5831333, + 48.0845012 + ], + [ + -4.63074, + 48.0702794 + ], + [ + -4.6669459, + 48.0767026 + ], + [ + -4.6820597, + 48.0633965 + ], + [ + -4.720531, + 48.0670675 + ], + [ + -4.7179337, + 48.0473329 + ], + [ + -4.7474681, + 48.0395287 + ], + [ + -4.5810723, + 47.9954359 + ], + [ + -4.5542798, + 47.9949764 + ], + [ + -4.5487839, + 48.004625 + ], + [ + -4.5037158, + 47.9940574 + ], + [ + -4.4274782, + 47.9582033 + ], + [ + -4.3856021, + 47.9034548 + ], + [ + -4.3543661, + 47.831135 + ], + [ + -4.3698117, + 47.8279082 + ], + [ + -4.3862891, + 47.8196098 + ], + [ + -4.3849213, + 47.8053149 + ], + [ + -4.3708538, + 47.7919388 + ], + [ + -4.2606289, + 47.7850188 + ], + [ + -4.177123, + 47.7960904 + ], + [ + -4.1624502, + 47.8122323 + ], + [ + -4.1569512, + 47.8339006 + ], + [ + -4.1640702, + 47.841275 + ], + [ + -4.1397885, + 47.8564814 + ], + [ + -4.1226196, + 47.8578636 + ], + [ + -4.1049892, + 47.8583243 + ], + [ + -4.0656243, + 47.853717 + ], + [ + -4.0525814, + 47.8477268 + ], + [ + -4.0409098, + 47.8389707 + ], + [ + -4.0072652, + 47.8454227 + ], + [ + -3.9674384, + 47.84957 + ], + [ + -3.9724004, + 47.8790524 + ], + [ + -3.9591983, + 47.8878018 + ], + [ + -3.9442339, + 47.8790524 + ], + [ + -3.9049564, + 47.8315959 + ], + [ + -3.8788605, + 47.8154601 + ], + [ + -3.8521774, + 47.7873255 + ], + [ + -3.7876004, + 47.7822505 + ], + [ + -3.7285194, + 47.7970129 + ], + [ + -3.6742473, + 47.7720989 + ], + [ + -3.5292927, + 47.7573294 + ], + [ + -3.4998373, + 47.7226969 + ], + [ + -3.450289, + 47.6894281 + ], + [ + -3.3988953, + 47.6945122 + ], + [ + -3.3748637, + 47.6838812 + ], + [ + -3.3472407, + 47.685268 + ], + [ + -3.3137526, + 47.6871169 + ], + [ + -3.2746124, + 47.6741727 + ], + [ + -3.2279154, + 47.6464244 + ], + [ + -3.2022861, + 47.6186613 + ], + [ + -3.1679367, + 47.606163 + ], + [ + -3.1475789, + 47.5797681 + ], + [ + -3.1433571, + 47.5422366 + ], + [ + -3.1639668, + 47.527402 + ], + [ + -3.150227, + 47.4838009 + ], + [ + -3.131795, + 47.4689497 + ], + [ + -3.1022658, + 47.464772 + ], + [ + -3.0677883, + 47.467093 + ], + [ + -3.0912781, + 47.488441 + ], + [ + -3.1153247, + 47.5158095 + ], + [ + -3.1194347, + 47.539919 + ], + [ + -3.1070709, + 47.5570671 + ], + [ + -3.0590061, + 47.5617008 + ], + [ + -3.0095714, + 47.5607741 + ], + [ + -2.9491407, + 47.5297202 + ], + [ + -2.8961654, + 47.5301838 + ], + [ + -2.858381, + 47.5079251 + ], + [ + -2.8543651, + 47.4944726 + ], + [ + -2.8207397, + 47.485193 + ], + [ + -2.7877272, + 47.4800885 + ], + [ + -2.776804, + 47.4916889 + ], + [ + -2.7500142, + 47.4940086 + ], + [ + -2.7273573, + 47.5005034 + ], + [ + -2.704698, + 47.4986479 + ], + [ + -2.67987, + 47.485193 + ], + [ + -2.6654432, + 47.4921528 + ], + [ + -2.6523905, + 47.5005034 + ], + [ + -2.6400247, + 47.49772 + ], + [ + -2.6043013, + 47.4926168 + ], + [ + -2.5891875, + 47.5097803 + ], + [ + -2.5452202, + 47.5051421 + ], + [ + -2.5342284, + 47.5102442 + ], + [ + -2.5108708, + 47.4916889 + ], + [ + -2.497131, + 47.4666288 + ], + [ + -2.5101838, + 47.4554872 + ], + [ + -2.4875132, + 47.4355192 + ], + [ + -2.4662165, + 47.4392347 + ], + [ + -2.4586597, + 47.4331968 + ], + [ + -2.5076273, + 47.4039264 + ], + [ + -2.5646474, + 47.3792894 + ], + [ + -2.5649597, + 47.3723146 + ], + [ + -2.5323459, + 47.354641 + ], + [ + -2.5113745, + 47.3341694 + ], + [ + -2.5141225, + 47.3081031 + ], + [ + -2.5539678, + 47.3025158 + ], + [ + -2.5539678, + 47.2857503 + ], + [ + -2.4179441, + 47.255932 + ], + [ + -2.3739768, + 47.2699114 + ], + [ + -2.2956601, + 47.2242315 + ], + [ + -2.2654326, + 47.2335572 + ], + [ + -2.2283352, + 47.2410165 + ], + [ + -2.1912378, + 47.2326247 + ], + [ + -2.1678788, + 47.2027764 + ], + [ + -2.1761227, + 47.1794457 + ], + [ + -2.2056715, + 47.1701106 + ], + [ + -2.2413949, + 47.1542371 + ], + [ + -2.2413949, + 47.1271478 + ], + [ + -2.1603302, + 47.1168689 + ], + [ + -2.112241, + 47.0841503 + ], + [ + -2.0985012, + 47.0701218 + ], + [ + -2.0916313, + 47.0345666 + ], + [ + -2.1328507, + 47.0177164 + ], + [ + -2.2056715, + 47.0083528 + ], + [ + -2.2276551, + 47.0205251 + ], + [ + -2.2647525, + 47.0345666 + ], + [ + -2.3059718, + 47.0280143 + ], + [ + -2.3087198, + 46.9858736 + ], + [ + -2.2178588, + 46.9324477 + ], + [ + -2.1520863, + 46.8141588 + ], + [ + -1.9459335, + 46.6748833 + ], + [ + -1.8182099, + 46.4898835 + ], + [ + -1.6450887, + 46.4085109 + ], + [ + -1.5145609, + 46.391464 + ], + [ + -1.4692196, + 46.3308098 + ], + [ + -1.3719328, + 46.3402914 + ], + [ + -1.3156245, + 46.2786312 + ], + [ + -1.213443, + 46.2710375 + ], + [ + -0.7627784, + 46.2862239 + ], + [ + -0.5154625, + 46.2900199 + ], + [ + -0.0153347, + 46.1075173 + ], + [ + 0.5617357, + 46.050361 + ], + [ + 0.8578423, + 46.1493944 + ], + [ + 1.2535477, + 46.362093 + ], + [ + 1.8086346, + 46.5588603 + ], + [ + 2.3582255, + 46.6720565 + ], + [ + 3.6882355, + 46.743625 + ], + [ + 4.7544419, + 46.8714582 + ], + [ + 5.3699838, + 46.9089984 + ], + [ + 5.5073815, + 47.0663803 + ], + [ + 5.8646156, + 47.2344896 + ], + [ + 6.4197024, + 47.349989 + ], + [ + 7.2990479, + 47.3871931 + ], + [ + 7.5142861, + 47.639485 + ] + ] + ], + "type": "Polygon" +} \ No newline at end of file diff --git a/examples/energy_market_outlooks/polygons/northern_france.json b/examples/energy_market_outlooks/polygons/northern_france.json new file mode 100644 index 0000000..01e2070 --- /dev/null +++ b/examples/energy_market_outlooks/polygons/northern_france.json @@ -0,0 +1,1087 @@ +{ + "coordinates": [ + [ + [ + 7.5142861, + 47.639485 + ], + [ + 7.6311716, + 47.883197 + ], + [ + 7.8125366, + 48.1917256 + ], + [ + 7.9389425, + 48.4583519 + ], + [ + 8.1477871, + 48.7090879 + ], + [ + 8.4099985, + 49.0378679 + ], + [ + 8.0309784, + 49.1996535 + ], + [ + 7.2564176, + 49.42884 + ], + [ + 6.2401959, + 49.582226 + ], + [ + 5.207525, + 49.8025412 + ], + [ + 4.1576554, + 49.9618223 + ], + [ + 3.817642, + 50.0253876 + ], + [ + 3.6695242, + 50.2507175 + ], + [ + 3.6008432, + 50.4819783 + ], + [ + 3.3014232, + 50.4924633 + ], + [ + 3.3205518, + 50.6616494 + ], + [ + 3.2355258, + 50.7746817 + ], + [ + 2.9305742, + 50.7885745 + ], + [ + 2.7409928, + 50.8683781 + ], + [ + 2.5156053, + 51.0823905 + ], + [ + 2.1705074, + 51.0508911 + ], + [ + 1.9383913, + 51.0068423 + ], + [ + 1.7413687, + 50.9497758 + ], + [ + 1.5438481, + 50.8857087 + ], + [ + 1.5216229, + 50.4679945 + ], + [ + 1.4339794, + 50.1557859 + ], + [ + 1.0876877, + 49.9618223 + ], + [ + 0.576645, + 49.8875565 + ], + [ + 0.2640013, + 49.7373503 + ], + [ + 0.1867676, + 49.7240353 + ], + [ + 0.1101158, + 49.6782925 + ], + [ + 0.0558437, + 49.5073767 + ], + [ + 0.1027103, + 49.4020374 + ], + [ + 0.0013853, + 49.3407831 + ], + [ + -0.147624, + 49.3094559 + ], + [ + -0.2863332, + 49.3134848 + ], + [ + -0.4173401, + 49.3591227 + ], + [ + -0.5306932, + 49.367172 + ], + [ + -0.7601279, + 49.3680663 + ], + [ + -0.9435734, + 49.4033778 + ], + [ + -1.0610485, + 49.4091862 + ], + [ + -1.1290604, + 49.417674 + ], + [ + -1.1867674, + 49.456967 + ], + [ + -1.2540923, + 49.5510527 + ], + [ + -1.2066901, + 49.6013655 + ], + [ + -1.214247, + 49.6338409 + ], + [ + -1.2279867, + 49.6645168 + ], + [ + -1.2485964, + 49.6956177 + ], + [ + -1.2815719, + 49.7071643 + ], + [ + -1.3619495, + 49.7138246 + ], + [ + -1.4320224, + 49.7111606 + ], + [ + -1.4670588, + 49.7111606 + ], + [ + -1.5275138, + 49.6716274 + ], + [ + -1.5673591, + 49.6809583 + ], + [ + -1.7136877, + 49.6880664 + ], + [ + -1.8112401, + 49.6925085 + ], + [ + -1.8229189, + 49.7107166 + ], + [ + -1.8490245, + 49.7240353 + ], + [ + -1.878565, + 49.7164885 + ], + [ + -1.9136014, + 49.7364628 + ], + [ + -1.9445159, + 49.7333562 + ], + [ + -1.9678735, + 49.7156005 + ], + [ + -1.9521479, + 49.6680722 + ], + [ + -1.877958, + 49.6449576 + ], + [ + -1.8580253, + 49.6138244 + ], + [ + -1.8669836, + 49.5608522 + ], + [ + -1.8978007, + 49.5274368 + ], + [ + -1.8435286, + 49.3761142 + ], + [ + -1.7975003, + 49.3479408 + ], + [ + -1.6295923, + 49.1704813 + ], + [ + -1.6467671, + 49.1075903 + ], + [ + -1.6069217, + 48.9644414 + ], + [ + -1.6254704, + 48.8317296 + ], + [ + -1.586999, + 48.8082201 + ], + [ + -1.5725723, + 48.7199612 + ], + [ + -1.6131046, + 48.6914137 + ], + [ + -1.6344013, + 48.6419836 + ], + [ + -1.7200247, + 48.6347237 + ], + [ + -1.7887236, + 48.6329086 + ], + [ + -1.8107072, + 48.6474278 + ], + [ + -1.7997154, + 48.6837076 + ], + [ + -1.8436827, + 48.7127126 + ], + [ + -1.9673406, + 48.6982122 + ], + [ + -2.0607711, + 48.6456131 + ], + [ + -2.1899249, + 48.6401687 + ], + [ + -2.2613718, + 48.6619428 + ], + [ + -2.3135829, + 48.7018377 + ], + [ + -2.4592245, + 48.665571 + ], + [ + -2.5405513, + 48.6310934 + ], + [ + -2.6641805, + 48.5511601 + ], + [ + -2.7972229, + 48.6691988 + ], + [ + -2.912637, + 48.754378 + ], + [ + -2.9538563, + 48.8104811 + ], + [ + -2.973092, + 48.8683282 + ], + [ + -3.1077418, + 48.8918096 + ], + [ + -3.2313997, + 48.8791671 + ], + [ + -3.4182607, + 48.8357975 + ], + [ + -3.5089432, + 48.8538727 + ], + [ + -3.6326011, + 48.7978182 + ], + [ + -3.6243573, + 48.7054629 + ], + [ + -3.8304539, + 48.7380783 + ], + [ + -3.8551854, + 48.7090879 + ], + [ + -3.9376241, + 48.7054629 + ], + [ + -3.9486159, + 48.7489453 + ], + [ + -4.0228107, + 48.7688622 + ], + [ + -4.0813948, + 48.6927734 + ], + [ + -4.1638184, + 48.7018377 + ], + [ + -4.2434981, + 48.6746401 + ], + [ + -4.3470693, + 48.6818942 + ], + [ + -4.5971332, + 48.6292781 + ], + [ + -4.7757502, + 48.5438872 + ], + [ + -4.8010323, + 48.4547089 + ], + [ + -4.7974738, + 48.3525987 + ], + [ + -4.7672463, + 48.3188208 + ], + [ + -4.6937385, + 48.3261261 + ], + [ + -4.6745028, + 48.3370819 + ], + [ + -4.5975601, + 48.325213 + ], + [ + -4.6236656, + 48.2932443 + ], + [ + -4.6435883, + 48.2566842 + ], + [ + -4.5810723, + 48.2338209 + ], + [ + -4.5714545, + 48.1720387 + ], + [ + -4.5350441, + 48.1615055 + ], + [ + -4.4739021, + 48.229247 + ], + [ + -4.3179557, + 48.194472 + ], + [ + -4.2901491, + 48.1308093 + ], + [ + -4.3110858, + 48.094133 + ], + [ + -4.3749758, + 48.1138498 + ], + [ + -4.5831333, + 48.0845012 + ], + [ + -4.63074, + 48.0702794 + ], + [ + -4.6669459, + 48.0767026 + ], + [ + -4.6820597, + 48.0633965 + ], + [ + -4.720531, + 48.0670675 + ], + [ + -4.7179337, + 48.0473329 + ], + [ + -4.7474681, + 48.0395287 + ], + [ + -4.5810723, + 47.9954359 + ], + [ + -4.5542798, + 47.9949764 + ], + [ + -4.5487839, + 48.004625 + ], + [ + -4.5037158, + 47.9940574 + ], + [ + -4.4274782, + 47.9582033 + ], + [ + -4.3856021, + 47.9034548 + ], + [ + -4.3543661, + 47.831135 + ], + [ + -4.3698117, + 47.8279082 + ], + [ + -4.3862891, + 47.8196098 + ], + [ + -4.3849213, + 47.8053149 + ], + [ + -4.3708538, + 47.7919388 + ], + [ + -4.2606289, + 47.7850188 + ], + [ + -4.177123, + 47.7960904 + ], + [ + -4.1624502, + 47.8122323 + ], + [ + -4.1569512, + 47.8339006 + ], + [ + -4.1640702, + 47.841275 + ], + [ + -4.1397885, + 47.8564814 + ], + [ + -4.1226196, + 47.8578636 + ], + [ + -4.1049892, + 47.8583243 + ], + [ + -4.0656243, + 47.853717 + ], + [ + -4.0525814, + 47.8477268 + ], + [ + -4.0409098, + 47.8389707 + ], + [ + -4.0072652, + 47.8454227 + ], + [ + -3.9674384, + 47.84957 + ], + [ + -3.9724004, + 47.8790524 + ], + [ + -3.9591983, + 47.8878018 + ], + [ + -3.9442339, + 47.8790524 + ], + [ + -3.9049564, + 47.8315959 + ], + [ + -3.8788605, + 47.8154601 + ], + [ + -3.8521774, + 47.7873255 + ], + [ + -3.7876004, + 47.7822505 + ], + [ + -3.7285194, + 47.7970129 + ], + [ + -3.6742473, + 47.7720989 + ], + [ + -3.5292927, + 47.7573294 + ], + [ + -3.4998373, + 47.7226969 + ], + [ + -3.450289, + 47.6894281 + ], + [ + -3.3988953, + 47.6945122 + ], + [ + -3.3748637, + 47.6838812 + ], + [ + -3.3472407, + 47.685268 + ], + [ + -3.3137526, + 47.6871169 + ], + [ + -3.2746124, + 47.6741727 + ], + [ + -3.2279154, + 47.6464244 + ], + [ + -3.2022861, + 47.6186613 + ], + [ + -3.1679367, + 47.606163 + ], + [ + -3.1475789, + 47.5797681 + ], + [ + -3.1433571, + 47.5422366 + ], + [ + -3.1639668, + 47.527402 + ], + [ + -3.150227, + 47.4838009 + ], + [ + -3.131795, + 47.4689497 + ], + [ + -3.1022658, + 47.464772 + ], + [ + -3.0677883, + 47.467093 + ], + [ + -3.0912781, + 47.488441 + ], + [ + -3.1153247, + 47.5158095 + ], + [ + -3.1194347, + 47.539919 + ], + [ + -3.1070709, + 47.5570671 + ], + [ + -3.0590061, + 47.5617008 + ], + [ + -3.0095714, + 47.5607741 + ], + [ + -2.9491407, + 47.5297202 + ], + [ + -2.8961654, + 47.5301838 + ], + [ + -2.858381, + 47.5079251 + ], + [ + -2.8543651, + 47.4944726 + ], + [ + -2.8207397, + 47.485193 + ], + [ + -2.7877272, + 47.4800885 + ], + [ + -2.776804, + 47.4916889 + ], + [ + -2.7500142, + 47.4940086 + ], + [ + -2.7273573, + 47.5005034 + ], + [ + -2.704698, + 47.4986479 + ], + [ + -2.67987, + 47.485193 + ], + [ + -2.6654432, + 47.4921528 + ], + [ + -2.6523905, + 47.5005034 + ], + [ + -2.6400247, + 47.49772 + ], + [ + -2.6043013, + 47.4926168 + ], + [ + -2.5891875, + 47.5097803 + ], + [ + -2.5452202, + 47.5051421 + ], + [ + -2.5342284, + 47.5102442 + ], + [ + -2.5108708, + 47.4916889 + ], + [ + -2.497131, + 47.4666288 + ], + [ + -2.5101838, + 47.4554872 + ], + [ + -2.4875132, + 47.4355192 + ], + [ + -2.4662165, + 47.4392347 + ], + [ + -2.4586597, + 47.4331968 + ], + [ + -2.5076273, + 47.4039264 + ], + [ + -2.5646474, + 47.3792894 + ], + [ + -2.5649597, + 47.3723146 + ], + [ + -2.5323459, + 47.354641 + ], + [ + -2.5113745, + 47.3341694 + ], + [ + -2.5141225, + 47.3081031 + ], + [ + -2.5539678, + 47.3025158 + ], + [ + -2.5539678, + 47.2857503 + ], + [ + -2.4179441, + 47.255932 + ], + [ + -2.3739768, + 47.2699114 + ], + [ + -2.2956601, + 47.2242315 + ], + [ + -2.2654326, + 47.2335572 + ], + [ + -2.2283352, + 47.2410165 + ], + [ + -2.1912378, + 47.2326247 + ], + [ + -2.1678788, + 47.2027764 + ], + [ + -2.1761227, + 47.1794457 + ], + [ + -2.2056715, + 47.1701106 + ], + [ + -2.2413949, + 47.1542371 + ], + [ + -2.2413949, + 47.1271478 + ], + [ + -2.1603302, + 47.1168689 + ], + [ + -2.112241, + 47.0841503 + ], + [ + -2.0985012, + 47.0701218 + ], + [ + -2.0916313, + 47.0345666 + ], + [ + -2.1328507, + 47.0177164 + ], + [ + -2.2056715, + 47.0083528 + ], + [ + -2.2276551, + 47.0205251 + ], + [ + -2.2647525, + 47.0345666 + ], + [ + -2.3059718, + 47.0280143 + ], + [ + -2.3087198, + 46.9858736 + ], + [ + -2.2178588, + 46.9324477 + ], + [ + -2.1520863, + 46.8141588 + ], + [ + -1.9459335, + 46.6748833 + ], + [ + -1.8182099, + 46.4898835 + ], + [ + -1.6450887, + 46.4085109 + ], + [ + -1.5145609, + 46.391464 + ], + [ + -1.4692196, + 46.3308098 + ], + [ + -1.3719328, + 46.3402914 + ], + [ + -1.3156245, + 46.2786312 + ], + [ + -1.213443, + 46.2710375 + ], + [ + -0.7627784, + 46.2862239 + ], + [ + -0.5154625, + 46.2900199 + ], + [ + -0.0153347, + 46.1075173 + ], + [ + 0.5617357, + 46.050361 + ], + [ + 0.8578423, + 46.1493944 + ], + [ + 1.2535477, + 46.362093 + ], + [ + 1.8086346, + 46.5588603 + ], + [ + 2.3582255, + 46.6720565 + ], + [ + 3.6882355, + 46.743625 + ], + [ + 4.7544419, + 46.8714582 + ], + [ + 5.3699838, + 46.9089984 + ], + [ + 5.5073815, + 47.0663803 + ], + [ + 5.8646156, + 47.2344896 + ], + [ + 6.4197024, + 47.349989 + ], + [ + 7.2990479, + 47.3871931 + ], + [ + 7.5142861, + 47.639485 + ] + ] + ], + "type": "Polygon" +} \ No newline at end of file diff --git a/examples/energy_market_outlooks/requirements.txt b/examples/energy_market_outlooks/requirements.txt new file mode 100644 index 0000000..3577b41 --- /dev/null +++ b/examples/energy_market_outlooks/requirements.txt @@ -0,0 +1,5 @@ +shapely +matplotlib +cartopy +geopandas +xarray \ No newline at end of file