code: def generate_2d_layout(
      xy,
      w=0.07,
      h=0.05,
      pad=0.02,
      ch_names=None,
      ch_indices=None,
      name="ecog",
      bg_image=None,
      normalize=True,
  ):
      """Generate a custom 2D layout from xy points.
  
      Generates a 2-D layout for plotting with plot_topo methods and
      functions. XY points will be normalized between 0 and 1, where
      normalization extremes will be either the min/max of xy, or
      the width/height of bg_image.
  
      Parameters
      ----------
      xy : ndarray, shape (N, 2)
          The xy coordinates of sensor locations.
      w : float
          The width of each sensor's axis (between 0 and 1).
      h : float
          The height of each sensor's axis (between 0 and 1).
      pad : float
          Portion of the box to reserve for padding. The value can range between
          0.0 (boxes will touch, default) to 1.0 (boxes consist of only padding).
      ch_names : list
          The names of each channel. Must be a list of strings, with one
          string per channel.
      ch_indices : list
          Index of each channel - must be a collection of unique integers,
          one index per channel.
      name : str
          The name of this layout type.
      bg_image : path-like | ndarray
          The image over which sensor axes will be plotted. Either a path to an
          image file, or an array that can be plotted with plt.imshow. If
          provided, xy points will be normalized by the width/height of this
          image. If not, xy points will be normalized by their own min/max.
      normalize : bool
          Whether to normalize the coordinates to run from 0 to 1. Defaults to
          True.
  
      Returns
      -------
      layout : Layout
          A Layout object that can be plotted with plot_topo
          functions and methods.
  
      See Also
      --------
      make_eeg_layout, make_grid_layout
  
      Notes
      -----
      .. versionadded:: 0.9.0
      """
      import matplotlib.pyplot as plt
  
      if ch_indices is None:
          ch_indices = np.arange(xy.shape[0])
      if ch_names is None:
          ch_names = list(map(str, ch_indices))
  
      if len(ch_names) != len(ch_indices):
          raise ValueError("# channel names and indices must be equal")
      if len(ch_names) != len(xy):
          raise ValueError("# channel names and xy vals must be equal")
  
      x, y = xy.copy().astype(float).T
  
      # Normalize xy to 0-1
      if bg_image is not None:
          # Normalize by image dimensions
          img = (
              plt.imread(bg_image)
              if isinstance(bg_image, str)
              else bg_image
          )
          x /= img.shape[1]
          y /= img.shape[0]
      elif normalize:
          # Normalize x and y by their maxes
          for i_dim in [x, y]:
              i_dim -= i_dim.min(0)
              i_dim /= i_dim.max(0) - i_dim.min(0)
  
      # Create box and pos variable
      box = _box_size(np.vstack([x, y]).T, padding=pad)
      box = (0, 0, box[0], box[1])
      w, h = (np.array([i] * x.shape[0]) for i in [w, h])
      loc_params = np.vstack([x, y, w, h]).T
  
      layout = Layout(box, loc_params, ch_names, ch_indices, name)
      return layout
  
imports: import logging
  from collections import defaultdict
  from itertools import combinations
  from pathlib import Path
  import numpy
  from scipy.spatial.distance import pdist, squareform
  from .._fiff.constants import FIFF
  from .._fiff.meas_info import Info
  from .._fiff.pick import _FNIRS_CH_TYPES_SPLIT, _picks_to_idx, pick_types
  from ..transforms import _cart_to_sph, _pol_to_cart
  from ..utils import _check_ch_locs, _check_fname, _check_option, _check_sphere, _clean_names, fill_doc, logger, warn
  from ..viz.topomap import plot_layout
  from .channels import _get_ch_info
label_desc: Use image node_id 18 for calling a global function with example usage: generate_2d_layout(xy, w, h, pad, ch_names, ch_indices, name, bg_image, normalize) and returns: layout