예제 #1
0
 def init_fig(self, fig, reward, done, timestamp):
     if fig is None:
         fig, ax = plt.subplots(1, 1, figsize=self.figsize)
     elif isinstance(fig, tuple):
         if len(fig) != 2:
             raise PlotError(
                 "PlotMatplotlib \"fig\" argument should be, if a tuple, a tuple containing a figure "
                 "and an axe, for example the results of `plt.subplots(1, 1)`. You provided "
                 "a tuple of length {}".format(len(fig)))
         fig, ax = fig
         if not isinstance(fig, self.accepted_figure_class):
             raise PlotError(
                 "PlotMatplotlib \"fig\" argument should be an object of type \"{}\" and not \"{}\"."
                 "".format(self.accepted_figure_class, type(fig)))
         if not isinstance(ax, self.accepted_figure_class_tuple[1]):
             raise PlotError(
                 "PlotMatplotlib \"fig\" argument should be an object of type \"{}\" and not \"{}\"."
                 "".format(self.accepted_figure_class, type(ax)))
     elif isinstance(fig, self.accepted_figure_class):
         ax = fig.gca()
     else:
         raise PlotError(
             "PlotMatplotlib \"fig\" argument should be an object of type \"{}\" and not \"{}\"."
             "".format(self.accepted_figure_class, type(fig)))
     return (fig, ax)
예제 #2
0
 def init_fig(self, fig, reward, done, timestamp):
     if fig is None:
         fig = go.Figure()
     elif not isinstance(fig, self.type_fig_allowed):
         raise PlotError(
             "PlotPlotly cannot plot on figure of type {}. The accepted type is {}. You provided an "
             "invalid argument for \"fig\"".format(type(fig),
                                                   self.type_fig_allowed))
     return fig
예제 #3
0
    def compute_grid_layout(self, observation_space, grid_layout=None):
        """
        Compute the grid layout from the observation space

        This should return a native python ``dict`` 
        in the same format as observation_space.grid_layout :

        .. code-block:: python

            {
              "substation1_name": [x_coord, y_coord],
              "substation2_name": [x_coord, y_coord],
              [...],
              "load1_name": [x_coord, y_coord],
              [...], 
              "gen1_name": [x_coord, y_coord],
              [...]
            }
        
        Note that is must contain at least the positions for the substations.
        The loads and generators will be skipped if missing. 

        By default, if `grid_layout` is provided this is returned, 
        otherwise returns observation_space.grid_layout

        Parameters
        ----------

        observation_space: ``grid2op.Observation.ObservationSpace``
             The observation space of the environment

        grid_layout: ``dict`` or ``None``
             A dictionary containing the coordinates for each substation.
        """
        # We need an intial layout to work with
        use_grid_layout = None
        if grid_layout is not None:
            use_grid_layout = grid_layout
        elif observation_space.grid_layout is not None:
            use_grid_layout = observation_space.grid_layout
        else:
            raise PlotError("No grid layout provided for plotting")

        # Compute loads and gens positions using a default implementation
        observation_space.grid_layout = use_grid_layout
        return layout_obs_sub_load_and_gen(observation_space,
                                           scale=self.scale,
                                           use_initial=True)
예제 #4
0
    def __init__(self,
                 observation_space,
                 display_mod="plotly",
                 substation_layout=None,
                 radius_sub=20.,
                 load_prod_dist=70.,
                 bus_radius=6.):
        if display_mod not in self.allwed_display_mod:
            raise PlotError(
                "Only avaible plot mod are \"{}\". You specified \"{}\" which is not supported."
                "".format(self.allwed_display_mod, display_mod))

        cls_ = self.allwed_display_mod[display_mod]
        self.displ_backend = cls_(observation_space,
                                  substation_layout=substation_layout,
                                  radius_sub=radius_sub,
                                  load_prod_dist=load_prod_dist,
                                  bus_radius=bus_radius)
        self.display_mod = display_mod
예제 #5
0
    def __init__(self,
                 observation_space,
                 substation_layout=None,
                 radius_sub=25.,
                 load_prod_dist=70.,
                 bus_radius=4.):
        """

        Parameters
        ----------
        substation_layout: ``list``
            List of tupe given the position of each of the substation of the powergrid.

        observation_space: :class:`grid2op.Observation.ObservationSpace`
            BaseObservation space

        """
        BasePlot.__init__(self,
                          substation_layout=substation_layout,
                          observation_space=observation_space,
                          radius_sub=radius_sub,
                          load_prod_dist=load_prod_dist,
                          bus_radius=bus_radius)
        if not can_plot:
            raise PlotError(
                "Impossible to plot as plotly cannot be imported. Please install \"plotly\" and "
                "\"seaborn\" with \"pip install --update plotly seaborn\"")

        # define a color palette, whatever...
        sns.set()
        pal = sns.light_palette("darkred", 8)
        self.cols = pal.as_hex()[1:]

        self.col_line = "royalblue"
        self.col_sub = "red"
        self.col_load = "black"
        self.col_gen = "darkgreen"
        self.default_color = "black"
        self.type_fig_allowed = go.Figure
예제 #6
0
    def __init__(self,
                 observation_space,
                 substation_layout=None,
                 radius_sub=20.,
                 load_prod_dist=70.,
                 bus_radius=6.):

        if substation_layout is None:
            if observation_space.grid_layout is None:
                # if no layout is provided, and observation_space has no layout, then it fails
                raise PlotError(
                    "Impossible to use plotting abilities without specifying a layout (coordinates) "
                    "of the substations.")

            # if no layout is provided, use the one in the observation_space
            substation_layout = []
            for el in observation_space.name_sub:
                substation_layout.append(observation_space.grid_layout[el])

        if len(substation_layout) != observation_space.n_sub:
            raise PlotError(
                "You provided a layout with {} elements while there are {} substations on the powergrid. "
                "Your layout is invalid".format(len(substation_layout),
                                                observation_space.n_sub))
        GridObjects.__init__(self)
        self.init_grid(observation_space)

        self.observation_space = observation_space
        self._layout = {}
        self._layout["substations"] = self._get_sub_layout(substation_layout)

        self.radius_sub = radius_sub
        self.load_prod_dist = load_prod_dist  # distance between load and generator to the center of the substation
        self.bus_radius = bus_radius

        self.subs_elements = [None for _ in self.observation_space.sub_info]

        # get the element in each substation
        for sub_id in range(self.observation_space.sub_info.shape[0]):
            this_sub = {}
            objs = self.observation_space.get_obj_connect_to(
                substation_id=sub_id)

            for c_id in objs["loads_id"]:
                c_nm = self._get_load_name(sub_id, c_id)
                this_load = {}
                this_load["type"] = "load"
                this_load["sub_pos"] = self.observation_space.load_to_sub_pos[
                    c_id]
                this_sub[c_nm] = this_load

            for g_id in objs["generators_id"]:
                g_nm = self._get_gen_name(sub_id, g_id)
                this_gen = {}
                this_gen["type"] = "gen"
                this_gen["sub_pos"] = self.observation_space.gen_to_sub_pos[
                    g_id]
                this_sub[g_nm] = this_gen

            for lor_id in objs["lines_or_id"]:
                ext_id = self.observation_space.line_ex_to_subid[lor_id]
                l_nm = self._get_line_name(sub_id, ext_id, lor_id)
                this_line = {}
                this_line["type"] = "line"
                this_line[
                    "sub_pos"] = self.observation_space.line_or_to_sub_pos[
                        lor_id]
                this_sub[l_nm] = this_line

            for lex_id in objs["lines_ex_id"]:
                or_id = self.observation_space.line_or_to_subid[lex_id]
                l_nm = self._get_line_name(or_id, sub_id, lex_id)
                this_line = {}
                this_line["type"] = "line"
                this_line[
                    "sub_pos"] = self.observation_space.line_ex_to_sub_pos[
                        lex_id]
                this_sub[l_nm] = this_line
            self.subs_elements[sub_id] = this_sub
        self._compute_layout()
예제 #7
0
    def plot_info(self,
                  figure=None,
                  redraw=True,
                  line_values=None,
                  line_unit="",
                  load_values=None,
                  load_unit="",
                  gen_values=None,
                  gen_unit="",
                  observation=None):
        """
        Plot an observation with custom values
        
        Parameters
        ----------
        figure: ``object``
            The figure on which to plot the observation. 
            If figure is ``None`` a new figure is created.

        line_values: ``list``
            information to be displayed for the powerlines
            [must have the same size as observation.n_line  and convertible to float]

        line_unit: ``str``
            Unit string for the :line_values: argument, displayed after the line value

        load_info: ``list``
            information to display for the loads
            [must have the same size as observation.n_load and convertible to float]

        load_unit: ``str``
            Unit string for the :load_values: argument, displayed after the load value

        gen_info: ``list``
            information to display in the generators
            [must have the same size as observation.n_gen and convertible to float]

        gen_unit: ``str``
            Unit string for the :gen_values: argument, displayed after the generator value

        observation: :class:`grid2op.Observation.BaseObservation`
            An observation to plot, can be None if no values are drawn from the observation

        Returns
        -------
        res: ``object``
            The figure updated with the data from the new observation.
        """
        # Check values are in the correct format
        if line_values is not None and len(
                line_values) != self.observation_space.n_line:
            raise PlotError(
                "Impossible to display these values on the powerlines: there are {} values"
                "provided for {} powerlines in the grid".format(
                    len(line_values), self.observation_space.n_line))
        if load_values is not None and len(
                load_values) != self.observation_space.n_load:
            raise PlotError(
                "Impossible to display these values on the loads: there are {} values"
                "provided for {} loads in the grid".format(
                    len(load_values), self.observation_space.n_load))
        if gen_values is not None and len(
                gen_values) != self.observation_space.n_gen:
            raise PlotError(
                "Impossible to display these values on the generators: there are {} values"
                "provided for {} generators in the grid".format(
                    len(gen_info), self.observation_space.n_gen))

        # Get a valid figure to draw into
        if figure is None:
            fig = self.create_figure()
            redraw = True
        elif redraw:
            self.clear_figure(figure)
            fig = figure
        else:
            fig = figure

        # Get a valid Observation
        if observation is None:
            # See dummy data added in the constructor
            observation = self.observation_space

        # Trigger draw calls
        self._plot_lines(fig, observation, line_values, line_unit, redraw)
        self._plot_loads(fig, observation, load_values, load_unit, redraw)
        self._plot_gens(fig, observation, gen_values, gen_unit, redraw)
        self._plot_subs(fig, observation, redraw)
        self._plot_legend(fig, observation, redraw)

        # Some implementations may need postprocessing
        self.plot_postprocess(fig, observation, not redraw)

        # Return updated figure
        return fig
예제 #8
0
    def plot_obs(self,
                 observation,
                 figure=None,
                 redraw=True,
                 line_info="rho",
                 load_info="p",
                 gen_info="p"):
        """
        Plot an observation.
        
        Parameters
        ----------
        observation: :class:`grid2op.Observation.BaseObservation`
            The observation to plot

        figure: ``object``
            The figure on which to plot the observation. 
            If figure is ``None``, a new figure is created.

        line_info: ``str``
            One of "rho", "a", or "p" or "v" 
            The information that will be plotted on the powerline.
            By default "rho".
            All flow are taken "origin" side.

        load_info: ``str``
            One of "p" or "v" the information displayed on the load.
            (default to "p").

        gen_info: ``str``
            One of "p" or "v" the information displayed on the generators
            (default to "p").

        Returns
        -------
        res: ``object``
            The figure updated with the data from the new observation.
        """

        # Start by checking arguments are valid
        if not isinstance(observation, BaseObservation):
            err_msg = "Observation is not a derived type of " \
                      "grid2op.Observation.BaseObservation"
            raise PlotError(err_msg)
        if line_info not in self._lines_info:
            err_msg = "Impossible to plot line info \"{}\" for line." \
                      " Possible values are {}"
            raise PlotError(err_msg.format(line_info, str(self._lines_info)))
        if load_info not in self._loads_info:
            err_msg = "Impossible to plot load info \"{}\" for line." \
                      " Possible values are {}"
            raise PlotError(err_msg.format(load_info, str(self._loads_info)))
        if gen_info not in self._gens_info:
            err_msg = "Impossible to plot gen info \"{}\" for line." \
                      " Possible values are {}"
            raise PlotError(err_msg.format(gen_info, str(self._gens_info)))

        line_values = None
        line_unit = ""
        if line_info is not None:
            line_unit = self._info_to_units[line_info]
        if line_info == "rho":
            line_values = observation.rho
        if line_info == "p":
            line_values = observation.p_or
        if line_info == "a":
            line_values = observation.a_or
        if line_info == "v":
            line_values = observation.v_or

        load_values = None
        load_unit = ""
        if load_info is not None:
            load_unit = self._info_to_units[load_info]
        if load_info == "p":
            load_values = copy.copy(observation.load_p) * -1.0
        if load_info == "v":
            load_values = observation.load_v

        gen_values = None
        gen_unit = ""
        if gen_info is not None:
            gen_unit = self._info_to_units[gen_info]
        if gen_info == "p":
            gen_values = observation.prod_p
        if gen_info == "v":
            gen_values = observation.prod_v

        return self.plot_info(observation=observation,
                              figure=figure,
                              redraw=redraw,
                              line_values=line_values,
                              line_unit=line_unit,
                              load_values=load_values,
                              load_unit=load_unit,
                              gen_values=gen_values,
                              gen_unit=gen_unit)
예제 #9
0
    def plot_info(self,
                  line_info=None,
                  load_info=None,
                  gen_info=None,
                  sub_info=None,
                  colormap=None):
        """
        Plot some information on the powergrid. For now, only numeric data are supported.

        Parameters
        ----------
        line_info: ``list``
            information to be displayed in the powerlines, in place of their name and id (for example their
            thermal limit) [must have the same size as the number of powerlines]
        load_info: ``list``
            information to display in the generators, in place of their name and id
            [must have the same size as the number of loads]
        gen_info: ``list``
            information to display in the generators, in place of their name and id (for example their pmax)
            [must have the same size as the number of generators]
        sub_info: ``list``
            information to display in the substation, in place of their name and id (for example the number of
            different topologies possible at this substation) [must have the same size as the number of substations]

        colormap: ``str``
            If not None, one of "line", "load", "gen" or "sub". If None, default colors will be used for each
            elements (default color is the coloring of
            If not None, all elements will be black, and the selected element will be highlighted.

        """
        fig, ax = plt.subplots(1, 1, figsize=(15, 15))

        if colormap is None:
            legend_help = [
                Line2D([0], [0], color=self.col_line, lw=4),
                Line2D([0], [0], color=self.col_sub, lw=4),
                Line2D([0], [0], color=self.col_load, lw=4),
                Line2D([0], [0], color=self.col_gen, lw=4)
            ]

        # draw powerline
        texts_line = None
        if line_info is not None:
            texts_line = [
                "{:.2f}".format(el) if el is not None else None
                for el in line_info
            ]
            if len(texts_line) != self.n_line:
                raise PlotError(
                    "Impossible to display these information on the powerlines: there are {} elements"
                    "provided while {} powerlines on this grid".format(
                        len(texts_line), self.n_line))
        self._draw_powerlines(ax, texts_line, colormap=colormap)

        # draw substation
        texts_sub = None
        if sub_info is not None:
            texts_sub = [
                "{:.2f}".format(el) if el is not None else None
                for el in sub_info
            ]
            if len(texts_sub) != self.n_sub:
                raise PlotError(
                    "Impossible to display these information on the substations: there are {} elements"
                    "provided while {} substations on this grid".format(
                        len(texts_sub), self.n_sub))
        self._draw_subs(ax, texts_sub, colormap=colormap)

        # draw loads
        texts_load = None
        if load_info is not None:
            texts_load = [
                "{:.2f}".format(el) if el is not None else None
                for el in load_info
            ]
            if len(texts_load) != self.n_load:
                raise PlotError(
                    "Impossible to display these information on the loads: there are {} elements"
                    "provided while {} loads on this grid".format(
                        len(texts_load), self.n_load))
        self._draw_loads(ax, texts_load, colormap=colormap)

        # draw gens
        texts_gen = None
        if gen_info is not None:
            texts_gen = [
                "{:.2f}".format(el) if el is not None else None
                for el in gen_info
            ]
            if len(texts_gen) != self.n_gen:
                raise PlotError(
                    "Impossible to display these information on the generators: there are {} elements"
                    "provided while {} generators on this grid".format(
                        len(texts_gen), self.n_gen))
        self._draw_gens(ax, texts_gen, colormap=colormap)

        if colormap is None:
            ax.legend(legend_help,
                      ["powerline", "substation", "load", "generator"])
        return fig
예제 #10
0
    def plot_obs(self,
                 observation,
                 fig=None,
                 reward=None,
                 done=None,
                 timestamp=None,
                 line_info="rho",
                 load_info="p",
                 gen_info="p",
                 colormap="line"):
        """
        .. warning:: /!\\\\ This module is deprecated /!\\\\

            Prefer using the module `grid2op.PlotGrid

        Plot the given observation in the given figure.

        For now it represents information about load and generator active values.
        It also display dashed powerlines when they are disconnected and the color of each powerlines depends on
        its relative flow (its flow in amperes divided by its maximum capacity).

        If a substation counts only 1 bus, nothing specific is display. If it counts more, then buses are materialized
        by colored dot and lines will connect every object to its appropriate bus (with the proper color).

        Names of substation and objects are NOT displayed on this figure to lower the amount of information.

        Parameters
        ----------
        observation: :class:`grid2op.Observation.Observation`
            The observation to plot

        fig: :class:`plotly.graph_objects.Figure`
            The figure on which to plot the observation. Possibly ``None``, in this case a new figure is made.

        line_info: ``str``
            One of "rho", "a", or "p" or "v" the information that will be plotted on the powerline By default "rho".
            All flow are taken "origin" side.

        load_info: ``str``
            One of "p" or "v" the information displayed on the load (défault to "p").

        gen_info: ``str``
            One of "p" or "v" the information displayed on the generators (default to "p").

        Returns
        -------
        res: :class:`plotly.graph_objects.Figure`
            The figure updated with the data from the new observation.
        """
        fig = self.init_fig(fig, reward, done, timestamp)

        # draw substation
        subs = self._draw_subs(fig=fig,
                               vals=[None for el in range(self.n_sub)])

        # draw powerlines
        if line_info == "rho":
            line_vals = [observation.rho]
            line_units = "%"
        elif line_info == "a":
            line_vals = [observation.a_or]
            line_units = "A"
        elif line_info == "p":
            line_vals = [observation.p_or]
            line_units = "MW"
        elif line_info == "v":
            line_vals = [observation.v_or]
            line_units = "kV"
        else:
            raise PlotError(
                "Impossible to plot value \"{}\" for line. Possible values are \"rho\", \"p\", \"v\" and \"a\"."
            )
        line_vals.append(observation.line_status)
        line_vals.append(observation.p_or)
        lines = self._draw_powerlines(fig,
                                      vals=line_vals,
                                      unit=line_units,
                                      colormap=colormap)

        # draw the loads
        if load_info == "p":
            loads_vals = -observation.load_p
            load_units = "MW"
        elif load_info == "v":
            loads_vals = observation.load_v
            load_units = "kV"
        else:
            raise PlotError(
                "Impossible to plot value \"{}\" for load. Possible values are \"p\" and \"v\"."
            )
        loads = self._draw_loads(fig,
                                 vals=loads_vals,
                                 unit=load_units,
                                 colormap=colormap)

        # draw the generators
        if gen_info == "p":
            gen_vals = observation.prod_p
            gen_units = "MW"
        elif gen_info == "v":
            gen_vals = observation.prod_v
            gen_units = "kV"
        else:
            raise PlotError(
                "Impossible to plot value \"{}\" for generators. Possible values are \"p\" and \"v\"."
            )
        gens = self._draw_gens(fig,
                               vals=gen_vals,
                               unit=gen_units,
                               colormap=colormap)
        # draw the topologies
        topos = self._draw_topos(fig=fig, observation=observation)
        self._post_process_obs(fig, reward, done, timestamp, subs, lines,
                               loads, gens, topos)
        return fig
예제 #11
0
    def plot_info(self,
                  fig=None,
                  line_info=None,
                  load_info=None,
                  gen_info=None,
                  sub_info=None,
                  colormap=None,
                  unit=None):
        """
        .. warning:: /!\\\\ This module is deprecated /!\\\\

            Prefer using the module `grid2op.PlotGrid

        Plot some information on the powergrid. For now, only numeric data are supported.

        Parameters
        ----------
        line_info: ``list``
            information to be displayed in the powerlines, in place of their name and id (for example their
            thermal limit) [must have the same size as the number of powerlines and convertible to float]

        load_info: ``list``
            information to display in the generators, in place of their name and id
            [must have the same size as the number of loads and convertible to float]

        gen_info: ``list``
            information to display in the generators, in place of their name and id (for example their pmax)
            [must have the same size as the number of generators and convertible to float]

        sub_info: ``list``
            information to display in the substation, in place of their name and id (for example the number of
            different topologies possible at this substation) [must have the same size as the number of substations,
            and convertible to float]

        colormap: ``str``
            If not None, one of "line", "load", "gen" or "sub". If None, default colors will be used for each
            elements (default color is the coloring of
            If not None, all elements will be black, and the selected element will be highlighted.

        fig: ``matplotlib figure``
            The figure on which to draw. It is created by the method if ``None``.

        unit: ``str``, optional
            The unit in which the data are provided. For example, if you provide in `line_info` some data in mega-watt
            (MW) you can add `unit="MW"` to have the unit display on the screen.

        """
        fig = self.init_fig(fig, reward=None, done=None, timestamp=None)

        # draw powerline
        unit_line = None
        if line_info is not None:
            unit_line = unit
            if len(line_info) != self.n_line:
                raise PlotError(
                    "Impossible to display these information on the powerlines: there are {} elements"
                    "provided while {} powerlines on this grid".format(
                        len(line_info), self.n_line))
            line_info = np.array(line_info).astype(np.float)
        line_info = [line_info, line_info, line_info]
        lines = self._draw_powerlines(fig,
                                      vals=line_info,
                                      colormap=colormap,
                                      unit=unit_line)

        # draw substation
        unit_sub = None
        if sub_info is not None:
            unit_sub = unit
            if len(sub_info) != self.n_sub:
                raise PlotError(
                    "Impossible to display these information on the substations: there are {} elements"
                    "provided while {} substations on this grid".format(
                        len(sub_info), self.n_sub))
            sub_info = np.array(sub_info).astype(np.float)
        subs = self._draw_subs(fig,
                               vals=sub_info,
                               colormap=colormap,
                               unit=unit_sub)

        # draw loads
        unit_load = None
        if load_info is not None:
            unit_load = unit
            if len(load_info) != self.n_load:
                raise PlotError(
                    "Impossible to display these information on the loads: there are {} elements"
                    "provided while {} loads on this grid".format(
                        len(load_info), self.n_load))
            load_info = np.array(load_info).astype(np.float)
        loads = self._draw_loads(fig,
                                 vals=load_info,
                                 colormap=colormap,
                                 unit=unit_load)

        # draw gens
        unit_gen = None
        if gen_info is not None:
            unit_gen = unit
            if len(gen_info) != self.n_gen:
                raise PlotError(
                    "Impossible to display these information on the generators: there are {} elements"
                    "provided while {} generators on this grid".format(
                        len(gen_info), self.n_gen))
            gen_info = np.array(gen_info).astype(np.float)
        gens = self._draw_gens(fig,
                               vals=gen_info,
                               colormap=colormap,
                               unit=unit_gen)

        self._post_process_obs(fig,
                               reward=None,
                               done=None,
                               timestamp=None,
                               subs=subs,
                               lines=lines,
                               loads=loads,
                               gens=gens,
                               topos=[])
        return fig
예제 #12
0
    def plot_info(self,
                  figure=None,
                  redraw=True,
                  line_values=None,
                  line_unit="",
                  load_values=None,
                  load_unit="",
                  storage_values=None,
                  storage_unit="",
                  gen_values=None,
                  gen_unit="",
                  observation=None,
                  coloring=None):
        """
        Plot an observation with custom values
        
        Parameters
        ----------
        figure: ``object``
            The figure on which to plot the observation. 
            If figure is ``None`` a new figure is created.

        line_values: ``list``
            information to be displayed for the powerlines
            [must have the same size as observation.n_line  and convertible to float]

        line_unit: ``str``
            Unit string for the :line_values: argument, displayed after the line value

        load_values: ``list``
            information to display for the loads
            [must have the same size as observation.n_load and convertible to float]

        load_unit: ``str``
            Unit string for the :load_values: argument, displayed after the load value

        storage_values: ``list``
            information to display for the storage units
            [must have the same size as observation.n_storage and convertible to float]

        storage_unit: ``str``
            Unit string for the :storage_values: argument, displayed after the storage value

        gen_values: ``list``
            information to display in the generators
            [must have the same size as observation.n_gen and convertible to float]

        gen_unit: ``str``
            Unit string for the :gen_values: argument, displayed after the generator value

        observation: :class:`grid2op.Observation.BaseObservation`
            An observation to plot, can be None if no values are drawn from the observation

        coloring:
            ``None`` for no special coloring, or "line" to color the powerline based on the value
            ("gen" and "load" coming soon)

        Examples
        --------

        More examples on how to use this function is given in the "8_PlottingCapabilities.ipynb" notebook.

        The basic concept is:

        .. code-block:: python

            import grid2op
            from grid2op.PlotGrid import PlotMatplot
            env = grid2op.make()
            plot_helper = PlotMatplot(env.observation_space)


            # plot the layout (position of each elements) of the powergrid
            plot_helper.plot_layout()

            # project some data on the grid
            line_values = env.get_thermal_limit()
            plot_helper.plot_info(line_values=line_values)

            # to plot an observation
            obs = env.reset()
            plot_helper.plot_obs(obs)


        Returns
        -------
        res: ``object``
            The figure updated with the data from the new observation.
        """
        # Check values are in the correct format
        if line_values is not None and len(
                line_values) != self.observation_space.n_line:
            raise PlotError(
                "Impossible to display these values on the powerlines: there are {} values"
                "provided for {} powerlines in the grid".format(
                    len(line_values), self.observation_space.n_line))
        if load_values is not None and len(
                load_values) != self.observation_space.n_load:
            raise PlotError(
                "Impossible to display these values on the loads: there are {} values"
                "provided for {} loads in the grid".format(
                    len(load_values), self.observation_space.n_load))
        if gen_values is not None and len(
                gen_values) != self.observation_space.n_gen:
            raise PlotError(
                "Impossible to display these values on the generators: there are {} values"
                "provided for {} generators in the grid".format(
                    len(gen_values), self.observation_space.n_gen))
        if storage_values is not None and len(
                storage_values) != self.observation_space.n_storage:
            raise PlotError(
                "Impossible to display these values on the storage units: there are {} values"
                "provided for {} generators in the grid".format(
                    len(storage_values), self.observation_space.n_storage))

        # Get a valid figure to draw into
        if figure is None:
            fig = self.create_figure()
            redraw = True
        elif redraw:
            self.clear_figure(figure)
            fig = figure
        else:
            fig = figure

        # Get a valid Observation
        if observation is None:
            # See dummy data added in the constructor
            observation = self.observation_space
            if coloring is not None:
                observation = copy.deepcopy(observation)
                if coloring == "line":
                    if line_values is None:
                        raise PlotError(
                            "Impossible to color the grid based on the line information (key word argument "
                            "\"line_values\") if this argument is None.")

                    observation.rho = copy.deepcopy(line_values)
                    try:
                        observation.rho = np.array(
                            observation.rho).astype(dt_float)
                    except:
                        raise PlotError(
                            "Impossible to convert the input values (line_values) to floating point"
                        )

                    # rescaling to have range 0 - 1.0
                    tmp = observation.rho[np.isfinite(observation.rho)]
                    observation.rho -= (
                        np.min(tmp) - 1e-1
                    )  # so the min is 1e-1 otherwise 0.0 is plotted as black
                    tmp = observation.rho[np.isfinite(observation.rho)]
                    observation.rho /= np.max(tmp)
                elif coloring == "load":
                    # TODO
                    warnings.warn(
                        "ooloring = loads is not available at the moment")
                elif coloring == "gen":
                    if gen_values is None:
                        raise PlotError(
                            "Impossible to color the grid based on the gen information (key word argument "
                            "\"gen_values\") if this argument is None.")

                    observation.prod_p = copy.deepcopy(gen_values)
                    try:
                        observation.prod_p = np.array(
                            observation.prod_p).astype(dt_float)
                    except:
                        raise PlotError(
                            "Impossible to convert the input values (gen_values) to floating point"
                        )

                    # rescaling to have range 0 - 1.0
                    tmp = observation.prod_p[np.isfinite(observation.prod_p)]
                    if np.any(np.isfinite(observation.prod_p)):
                        observation.prod_p -= (
                            np.min(tmp) - 1e-1
                        )  # so the min is 1e-1 otherwise 0.0 is plotted as black
                        tmp = observation.prod_p[np.isfinite(
                            observation.prod_p)]
                        observation.prod_p /= np.max(tmp)
                else:
                    raise PlotError(
                        "coloring must be one of \"line\", \"load\" or \"gen\""
                    )

        # Trigger draw calls
        self._plot_lines(fig, observation, line_values, line_unit, redraw)
        self._plot_loads(fig, observation, load_values, load_unit, redraw)
        self._plot_storages(fig, observation, storage_values, storage_unit,
                            redraw)
        self._plot_gens(fig, observation, gen_values, gen_unit, redraw)
        self._plot_subs(fig, observation, redraw)
        self._plot_legend(fig, observation, redraw)

        # Some implementations may need postprocessing
        self.plot_postprocess(fig, observation, not redraw)

        # Return updated figure
        return fig