1"""Utility functions for plotting."""
2
3import os
4
5import branca.colormap as cm
6import folium
7import geopandas as gpd
8import numpy as np
9import pandas as pd
10import seaborn as sns
11from branca.element import MacroElement
12from folium.plugins import (Fullscreen, GroupedLayerControl, MeasureControl,
13 MousePosition, StripePattern)
14from jinja2 import Template
15
16from h2ss import compare
17from h2ss import data as rd
18
19
[docs]
20class BindColormap(MacroElement):
21 """Binds a colormap to a given layer on Folium.
22
23 Parameters
24 ----------
25 colormap : branca.colormap.ColorMap
26 The colormap to bind.
27
28 Notes
29 -----
30 Source:
31 https://nbviewer.org/gist/BibMartin/f153aa957ddc5fadc64929abdee9ff2e
32 """
33
34 def __init__(self, layer, colormap):
35 super(BindColormap, self).__init__()
36 self.layer = layer
37 self.colormap = colormap
38 self._template = Template(
39 """
40 {% macro script(this, kwargs) %}
41 {{this.colormap.get_name()}}.svg[0][0].style.display = 'block';
42 {{this._parent.get_name()}}.on('overlayadd', function (eventLayer) {
43 if (eventLayer.layer == {{this.layer.get_name()}}) {
44 {{this.colormap.get_name()}}.svg[0][0].style.display = 'block';
45 }});
46 {{this._parent.get_name()}}.on('overlayremove', function (eventLayer) {
47 if (eventLayer.layer == {{this.layer.get_name()}}) {
48 {{this.colormap.get_name()}}.svg[0][0].style.display = 'none';
49 }});
50 {% endmacro %}
51 """
52 ) # noqa
53
54
[docs]
55def plot_interactive_map():
56 """Plot an interactive map of the results using Folium."""
57 ds, extent, exclusions = compare.load_all_data(keep_orig=True)
58
59 caverns, zones, weibull_df, injection_point = (
60 compare.optimisation_function(
61 ds=ds,
62 extent=extent,
63 exclusions=exclusions,
64 cavern_diameter=85,
65 cavern_height=120,
66 )
67 )
68
69 shape = rd.halite_shape(dat_xr=ds).buffer(1000).buffer(-1000)
70
71 col_list = [
72 "geometry",
73 "cavern_depth",
74 "capacity",
75 "Bathymetry",
76 "NetToGross",
77 "ThicknessNet",
78 "dist_Codling_Wind_Park",
79 "dist_Dublin_Array",
80 "dist_North_Irish_Sea_Array",
81 "LCOT_Codling_Wind_Park",
82 "LCOT_Dublin_Array",
83 "LCOT_North_Irish_Sea_Array",
84 ]
85 caverns_plt = caverns[col_list].copy()
86 caverns_plt["LCOT_mean"] = caverns_plt[
87 [
88 "LCOT_Codling_Wind_Park",
89 "LCOT_Dublin_Array",
90 "LCOT_North_Irish_Sea_Array",
91 ]
92 ].mean(axis=1)
93 for f in ["capacity", "LCOT_mean"]:
94 caverns_plt[f"{f}_str"] = caverns_plt[f].copy()
95 caverns_plt[f"{f}_str"] = [
96 f"{x:,.3f}" for x in caverns_plt[f"{f}_str"]
97 ]
98 for f in col_list:
99 if f == "NetToGross":
100 caverns_plt[f] = [f"{x * 100:.0f}" for x in caverns[f]]
101 elif f == "geometry":
102 caverns_plt[f] = caverns_plt[f].buffer(100)
103 elif f == "Bathymetry":
104 caverns_plt[f] = [f"{-x:.3f}" for x in caverns[f]]
105 elif f not in ["capacity", "LCOT_mean"]:
106 caverns_plt[f] = [f"{x:,.3f}" for x in caverns[f]]
107
108 # exclusions["wind_farms"] = pd.concat(
109 # [
110 # exclusions["wind_farms"][["geometry"]],
111 # weibull_df.drop(columns=["integral", "abserr"])
112 # ],
113 # axis=1
114 # )
115 exclusions["wind_farms"] = exclusions["wind_farms"][
116 ["name", "geometry", "cap"]
117 ]
118 exclusions["wind_farms"]["cap"] = [
119 f"{x:,.0f}" for x in exclusions["wind_farms"]["cap"]
120 ]
121
122 exclusions["shipwrecks"] = exclusions["shipwrecks"][
123 ["VESSELNAME", "VESSELTYPE", "geometry"]
124 ]
125 exclusions["shipwrecks"]["VESSELNAME"] = exclusions["shipwrecks"][
126 "VESSELNAME"
127 ].fillna("Unknown")
128
129 exclusions["wells"] = exclusions["wells"][
130 ["RIG_NAME", "OPERATOR", "geometry"]
131 ]
132
133 exclusions["cables"] = exclusions["cables"].dissolve()[["geometry"]]
134
135 exclusions["shipping"] = exclusions["shipping"].dissolve()[["geometry"]]
136
137 # create exclusion buffer
138 buffer = (
139 pd.concat(
140 [
141 exclusions["wells_b"],
142 exclusions["shipwrecks_b"],
143 exclusions["shipping_b"],
144 exclusions["cables_b"],
145 ]
146 )
147 .dissolve()
148 .overlay(gpd.GeoDataFrame(geometry=shape))
149 )
150
151 minx, miny, maxx, maxy = shape.to_crs(4326).bounds.values[0]
152 m = folium.Map(
153 tiles="cartodbvoyager",
154 control_scale=True,
155 location=[np.mean((miny, maxy)), np.mean((minx, maxx))],
156 zoom_start=10,
157 min_lat=miny,
158 max_lat=maxy,
159 min_lon=minx,
160 max_lon=maxx,
161 )
162
163 # shipwrecks
164 folium.GeoJson(
165 exclusions["shipwrecks"].to_crs(4326),
166 name="Shipwrecks",
167 tooltip=folium.GeoJsonTooltip(
168 fields=["VESSELNAME", "VESSELTYPE"],
169 aliases=["Shipwreck", "Vessel type"],
170 ),
171 marker=folium.Marker(icon=folium.Icon(icon="sailboat", prefix="fa")),
172 style_function=lambda feature: {"markerColor": "gray"},
173 ).add_to(m)
174
175 # exploration wells
176 folium.GeoJson(
177 exclusions["wells"].to_crs(4326),
178 name="Exploration wells",
179 marker=folium.Marker(icon=folium.Icon(icon="oil-well", prefix="fa")),
180 style_function=lambda feature: {"markerColor": "gray"},
181 tooltip=folium.GeoJsonTooltip(
182 fields=["RIG_NAME", "OPERATOR"],
183 aliases=["Exploration well", "Operator"],
184 ),
185 ).add_to(m)
186
187 # shipping routes
188 folium.GeoJson(
189 exclusions["shipping"].to_crs(4326),
190 name="Frequent shipping routes",
191 color="crimson",
192 tooltip="Frequent shipping route",
193 ).add_to(m)
194
195 # subsea cables
196 folium.GeoJson(
197 exclusions["cables"].to_crs(4326),
198 name="Subsea cables",
199 color="royalblue",
200 tooltip="Subsea cable",
201 ).add_to(m)
202
203 # wind farms
204 folium.GeoJson(
205 exclusions["wind_farms"].to_crs(4326),
206 name="Wind farms",
207 tooltip=folium.GeoJsonTooltip(
208 fields=["name", "cap"], aliases=["Wind farm", "Capacity [MW]"]
209 ),
210 style_function=lambda feature: {
211 "fillColor": "none",
212 "color": "seagreen",
213 "fillPattern": StripePattern(
214 angle=135, color="seagreen", space_color="none"
215 ),
216 "weight": 1,
217 },
218 ).add_to(m)
219
220 # caverns - capacity
221 fg1 = folium.FeatureGroup(name="Cavern capacity")
222 cm1 = cm.StepColormap(
223 list(sns.color_palette("rocket_r")),
224 vmin=caverns_plt["capacity"].min(),
225 vmax=caverns_plt["capacity"].max(),
226 )
227 caverns_dict = caverns_plt.reset_index().set_index("index")["capacity"]
228 cd1 = {key: cm1(caverns_dict[key]) for key in caverns_dict.keys()}
229 folium.GeoJson(
230 caverns_plt.to_crs(4326),
231 style_function=lambda feature: {
232 "fillColor": cd1[int(feature["id"])],
233 "color": "darkslategrey",
234 "weight": 0.5,
235 "fillOpacity": 1,
236 },
237 tooltip=folium.GeoJsonTooltip(
238 fields=[
239 "capacity_str",
240 "cavern_depth",
241 "ThicknessNet",
242 "NetToGross",
243 "Bathymetry",
244 ],
245 aliases=[
246 "Cavern capacity [GWh]",
247 "Cavern top depth [m]",
248 "Net halite thickness [m]",
249 "Net-to-gross ratio [%]",
250 "Water depth [m]",
251 ],
252 ),
253 smooth_factor=0,
254 ).add_to(fg1)
255 m.add_child(fg1)
256 cm1.caption = "Cavern capacity [GWh]"
257
258 # caverns - LCOT
259 fg2 = folium.FeatureGroup(name="Pipeline LCOT")
260 cm2 = cm.StepColormap(
261 list(sns.color_palette("mako_r")),
262 vmin=caverns_plt["LCOT_mean"].min(),
263 vmax=caverns_plt["LCOT_mean"].max(),
264 )
265 caverns_dict = caverns_plt.reset_index().set_index("index")["LCOT_mean"]
266 cd2 = {key: cm2(caverns_dict[key]) for key in caverns_dict.keys()}
267 folium.GeoJson(
268 caverns_plt.to_crs(4326),
269 style_function=lambda feature: {
270 "fillColor": cd2[int(feature["id"])],
271 "color": "darkslategrey",
272 "weight": 0.5,
273 "fillOpacity": 1,
274 },
275 tooltip=folium.GeoJsonTooltip(
276 fields=[
277 "LCOT_mean_str",
278 "LCOT_Codling_Wind_Park",
279 "LCOT_Dublin_Array",
280 "LCOT_North_Irish_Sea_Array",
281 ],
282 aliases=[
283 "Pipeline LCOT [€ kg⁻¹]<br>Mean",
284 "Codling Wind Park",
285 "Dublin Array",
286 "North Irish Sea Array",
287 ],
288 ),
289 smooth_factor=0,
290 ).add_to(fg2)
291 m.add_child(fg2)
292 cm2.caption = "Mean pipeline LCOT [€ kg⁻¹]"
293
294 # basin boundary
295 folium.GeoJson(
296 shape.to_crs(4326),
297 name="Kish Basin boundary",
298 style_function=lambda feature: {
299 "fillColor": "none",
300 "color": "darkslategrey",
301 "weight": 1,
302 },
303 tooltip="Kish Basin boundary",
304 ).add_to(m)
305
306 # exclusion buffer
307 folium.GeoJson(
308 buffer.to_crs(4326),
309 name="Exclusion buffer",
310 style_function=lambda feature: {
311 "fillPattern": StripePattern(
312 angle=45, color="slategrey", space_color="none"
313 ),
314 "color": "slategrey",
315 "weight": 0.5,
316 },
317 tooltip="Exclusion buffer",
318 ).add_to(m)
319
320 # zones of interest
321 folium.GeoJson(
322 zones.to_crs(4326),
323 name="Area of interest",
324 style_function=lambda feature: {
325 "fillColor": "none",
326 "color": "darkslategrey",
327 "weight": 0.5,
328 "dashArray": "5, 5",
329 },
330 tooltip="Area of interest",
331 ).add_to(m)
332
333 # injection point
334 folium.GeoJson(
335 injection_point.to_crs(4326),
336 name="Gas grid injection point",
337 tooltip="Gas grid injection point<br>Dublin Port",
338 marker=folium.Marker(icon=folium.Icon(icon="anchor", prefix="fa")),
339 style_function=lambda feature: {"markerColor": "red"},
340 ).add_to(m)
341
342 m.add_child(cm1).add_child(cm2)
343 m.add_child(BindColormap(fg1, cm1)).add_child(BindColormap(fg2, cm2))
344 folium.LayerControl(collapsed=False).add_to(m)
345 GroupedLayerControl(
346 groups={"Caverns": [fg1, fg2]}, collapsed=False
347 ).add_to(m)
348
349 MeasureControl().add_to(m)
350 MousePosition().add_to(m)
351 Fullscreen().add_to(m)
352
353 return m