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