<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Grid Generation with Interactive Widgets
This notebook demostrates how to use the interative widgets. 
See a version of it in action
Step1:  Main Tutorial
Step2:  Loading and plotting the boundary data
Step3:  Generating a grid with pygridgen, plotting with pygridtools
Step4:  Interactively manipulate the Focus
Step5:  Interactively change the number of nodes in the grid
(Notice how the focus stay where we want)
Step6:  Save, load, and recreate the altered grid without widgets
<ASSISTANT_TASK:>
Python Code:
from IPython.display import Audio,Image, YouTubeVideo
YouTubeVideo('S5SG9km2f_A', height=450, width=900)
Explanation: Grid Generation with Interactive Widgets
This notebook demostrates how to use the interative widgets. 
See a version of it in action:
End of explanation
%matplotlib inline
import warnings
warnings.simplefilter('ignore')
import numpy as np
import matplotlib.pyplot as plt
import pandas
import geopandas
from pygridgen import Gridgen
from pygridtools import viz, iotools
def plotter(x, y, **kwargs):
    figsize = kwargs.pop('figsize', (9, 9))
    fig, ax = plt.subplots(figsize=figsize)
    ax.set_aspect('equal')
    viz.plot_domain(domain, betacol='beta', ax=ax)
    ax.set_xlim([0, 25])
    ax.set_ylim([0, 25])
    return viz.plot_cells(x, y, ax=ax, **kwargs)
Explanation: Main Tutorial
End of explanation
domain = geopandas.read_file('basic_data/domain.geojson')
fig, ax = plt.subplots(figsize=(9, 9), subplot_kw={'aspect':'equal'})
fig = viz.plot_domain(domain, betacol='beta', ax=ax)
Explanation: Loading and plotting the boundary data
End of explanation
grid = Gridgen(domain.geometry.x, domain.geometry.y,
               domain.beta, shape=(50, 50), ul_idx=2)
fig_orig, artists = plotter(grid.x, grid.y)
Explanation: Generating a grid with pygridgen, plotting with pygridtools
End of explanation
focus, focuser_widget = iotools.interactive_grid_focus(grid, n_points=3, plotfxn=plotter)
focuser_widget
Explanation: Interactively manipulate the Focus
End of explanation
reshaped, shaper_widget = iotools.interactive_grid_shape(grid, max_n=100, plotfxn=plotter)
shaper_widget
fig_orig
Explanation: Interactively change the number of nodes in the grid
(Notice how the focus stay where we want)
End of explanation
import json
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as td:
    f = Path(td, 'widget_grid.json')
    with f.open('w') as grid_write:
        json.dump(grid.to_spec(), grid_write)
        
    with f.open('r') as grid_read:
        spec = json.load(grid_read)
        
new_grid = Gridgen.from_spec(spec)
plotter(new_grid.x, new_grid.y)
Explanation: Save, load, and recreate the altered grid without widgets
End of explanation
<END_TASK>