Source code for h2ss.plotting

  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