Example #1
0
def _diagnose(paths, cells):
    if cells is None:
        return
    import matplotlib.pyplot as plt
    import tramway.plot.mesh as mesh
    f = plt.figure()
    mesh.plot_delaunay(cells, axes=f.gca())
    colors = 'bgkyr'
    for p, c in zip(paths, colors):
        x = cells.tesselation.cell_centers[p]
        plt.plot(x[:, 0], x[:, 1], c, linestyle='-', linewidth=3)
    plt.show()
Example #2
0
    def plot_cell_indices(self,
                          tessellation,
                          axes=None,
                          voronoi_options=dict(),
                          delaunay_options=None,
                          aspect="equal",
                          title=None,
                          **kwargs):
        """
        Plot the Voronoi graph and Voronoi cell indices.

        The placement of the text elements is not optimized.
        """
        from tramway.plot.mesh import plot_delaunay, plot_voronoi, plot_cell_indices

        try:
            voronoi_options = dict(centroid_style=None,
                                   color="rrrr") | voronoi_options
        except TypeError:  # Python < 3.9
            voronoi_options, _options = (
                dict(centroid_style=None, color="rrrr"),
                voronoi_options,
            )
            voronoi_options.update(_options)
        if isinstance(tessellation, Analysis):
            tessellation = tessellation.data
        if isinstance(tessellation, Partition):
            sampling = tessellation
            tessellation = sampling.tessellation
        else:
            sampling = None
        if isinstance(tessellation, TimeLattice):
            if tessellation.spatial_mesh is None:
                raise TypeError("no tessellation found; only time segments")
            tessellation = tessellation.spatial_mesh
        if isinstance(tessellation, Voronoi):
            if sampling is None:
                sampling = Partition(locations, tessellation)
        else:
            raise TypeError("tessellation type not supported: {}".format(
                type(tessellation)))
        if delaunay_options:
            plot_delaunay(sampling, axes=axes, **delaunay_options)
        if voronoi_options:
            plot_voronoi(sampling, axes=axes, **voronoi_options)
        plot_cell_indices(tessellation, axes=axes, **kwargs)
        if title:
            axes.set_title(title)
        if aspect:
            axes.set_aspect(aspect)
Example #3
0
def scalar_map_2d(
    cells,
    values,
    aspect=None,
    clim=None,
    figure=None,
    axes=None,
    linewidth=1,
    delaunay=False,
    colorbar=True,
    alpha=None,
    colormap=None,
    unit=None,
    clabel=None,
    xlim=None,
    ylim=None,
    try_fix_corners=True,
    return_patches=False,
    **kwargs
):
    """
    Plot a 2D scalar map as a colourful image.

    Arguments:

        cells (Partition or FiniteElements): spatial description of the cells

        values (pandas.DataFrame or numpy.ndarray): feature value at each cell,
            that will be represented as a colour

        aspect (str): passed to :func:`~matplotlib.axes.Axes.set_aspect`

        clim (2-element sequence): passed to :func:`~matplotlib.cm.ScalarMappable.set_clim`

        figure (matplotlib.figure.Figure): figure handle

        axes (matplotlib.axes.Axes): axes handle

        linewidth (int): cell border line width

        delaunay (bool or dict): overlay the Delaunay graph; if ``dict``, options are passed
            to :func:`~tramway.plot.mesh.plot_delaunay`

        colorbar (bool or str or dict): add a colour bar; if ``dict``, options are passed to
            :func:`~matplotlib.pyplot.colorbar`;
            setting colorbar to '*nice*' allows to produce a colorbar close to the figure
            of the same size as the figure

        unit/clabel (str): colorbar label, usually the unit of displayed feature

        alpha (float): alpha value of the cells

        colormap (str): colormap name; see also https://matplotlib.org/users/colormaps.html

        xlim (2-element sequence): lower and upper x-axis bounds

        ylim (2-element sequence): lower and upper y-axis bounds

        return_patches (bool): returns `PatchCollection` patches and the corresponding
            bin indices.

    Extra keyword arguments are passed to :func:`~matplotlib.collections.PatchCollection`.

    """
    coords = None
    if isinstance(values, pd.DataFrame):
        if values.shape[1] != 1:
            coords = values[[col for col in "xyzt" if col in values.columns]]
            values = values[[col for col in values.columns if col not in "xyzt"]]
            if values.shape[1] != 1:
                warn(
                    "multiple parameters available; mapping first one only", UserWarning
                )
        values = values.iloc[:, 0]  # to Series
    # values = pd.to_numeric(values, errors='coerce')

    # parse Delaunay-related arguments
    if delaunay:
        if not isinstance(delaunay, dict):
            delaunay = {}
        if linewidth and "linewidth" not in delaunay:
            delaunay["linewidth"] = linewidth

    # turn the cells into polygons
    ids = []
    polygons = []
    if isinstance(cells, FiniteElements):

        ix, xy, ok = zip(*[(i, c.center, bool(c)) for i, c in cells.items()])
        ix, xy, ok = np.array(ix), np.array(xy), np.array(ok)
        if not (coords is None or np.all(np.isclose(xy, coords))):  # debug
            print("coordinates mismatch")
            print("in map:")
            print(xy)
            print("in cells:")
            print(coords)
        rp = bb = voronoi = None
        for c in range(xy.shape[0]):
            if ok[c]:
                vertices, voronoi, bb, rp = cell_to_polygon(
                    c, xy, voronoi, bb, rp, True
                )
                polygons.append(Polygon(vertices, True))

    elif isinstance(cells, Partition) and isinstance(cells.tessellation, Voronoi):

        xy = cells.tessellation.cell_centers
        # copy/paste from below
        if not xlim or not ylim:
            xy_min, _, xy_max, _ = _bounding_box(cells, xy)
            if not xlim:
                xlim = (xy_min[0], xy_max[0])
            if not ylim:
                ylim = (xy_min[1], xy_max[1])
        ix = np.arange(xy.shape[0])
        from tramway.tessellation.hexagon import HexagonalMesh
        from tramway.tessellation.kdtree import KDTreeMesh

        if isinstance(cells.tessellation, (RegularMesh, HexagonalMesh, KDTreeMesh)):
            vertices, cell_vertices, Av = (
                cells.tessellation.vertices,
                cells.tessellation.cell_vertices,
                cells.tessellation.vertex_adjacency.tocsr(),
            )
        else:
            try:
                vertices, cell_vertices, Av = box_voronoi_2d(
                    cells.tessellation, xlim, ylim
                )
            except AssertionError:
                warn("could not fix the borders", RuntimeWarning)
                vertices, cell_vertices, Av = (
                    cells.tessellation.vertices,
                    cells.tessellation.cell_vertices,
                    cells.tessellation.vertex_adjacency.tocsr(),
                )
        try:
            ok = 0 < cells.location_count
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            print(traceback.format_exc())
            ok = np.ones(ix.size, dtype=bool)
        if cells.tessellation.cell_label is not None:
            ok = np.logical_and(ok, 0 < cells.tessellation.cell_label)
        map_defined = np.zeros_like(ok)
        map_defined[values.index] = True
        ok[np.logical_not(map_defined)] = False
        ok[ok] = np.logical_not(np.isnan(values.loc[ix[ok]].values))
        for i in ix[ok]:
            extra_polygons = cell_to_polygon_(
                i, vertices, cell_vertices, Av, xlim, ylim, try_fix_corners
            )
            for extra_ix, extra_vs in extra_polygons:
                ids.append(extra_ix)
                polygons.append(Polygon(extra_vs, True))
    else:
        _type = repr(type(cells))
        if _type.endswith("'>"):
            _type = _type.split("'")[1]
        try:
            _nested_type = repr(type(cells.tessellation))
            if _nested_type.endswith("'>"):
                _nested_type = _nested_type.split("'")[1]
            raise TypeError(
                "wrong type for `cells`: {}<{}>".format(_type, _nested_type)
            )
        except AttributeError:
            raise TypeError("wrong type for `cells`: {}".format(_type))

    if not ids:
        ids = ix[ok]
    scalar_map = values.loc[ids].values

    # print(np.nonzero(~ok)[0])

    try:
        if np.any(np.isnan(scalar_map)):
            # print(np.nonzero(np.isnan(scalar_map)))
            msg = "NaN found"
            try:
                warn(msg, NaNWarning)
            except:
                print("warning: {}".format(msg))
            scalar_map[np.isnan(scalar_map)] = 0
    except TypeError:  # help debug
        print(scalar_map)
        print(scalar_map.dtype)
        raise

    if figure is None:
        import matplotlib.pyplot as plt

        figure = plt.gcf()  # before PatchCollection
    if axes is None:
        axes = figure.gca()
    # draw patches
    patch_kwargs = kwargs
    if alpha is not False:
        if alpha is None:
            alpha = 0.9
        patch_kwargs["alpha"] = alpha
    if colormap is not None:
        cmap = patch_kwargs.get("cmap", None)
        if cmap is None:
            patch_kwargs["cmap"] = colormap
        elif colormap != cmap:
            warn(
                "both cmap and colormap arguments are passed with different values",
                RuntimeWarning,
            )
    patches = PatchCollection(polygons, linewidth=linewidth, **patch_kwargs)
    patches.set_array(scalar_map)
    if clim is not None:
        patches.set_clim(clim)
    axes.add_collection(patches)

    obj = None
    if delaunay or isinstance(delaunay, dict):
        if not isinstance(delaunay, dict):
            delaunay = {}
        try:
            import tramway.plot.mesh as mesh

            obj = mesh.plot_delaunay(cells, centroid_style=None, axes=axes, **delaunay)
        except:
            import traceback

            traceback.print_exc()

    if not xlim or not ylim:
        xy_min, _, xy_max, _ = _bounding_box(cells, xy)
        if not xlim:
            xlim = (xy_min[0], xy_max[0])
        if not ylim:
            ylim = (xy_min[1], xy_max[1])
    axes.set_xlim(*xlim)
    axes.set_ylim(*ylim)
    if aspect is not None:
        axes.set_aspect(aspect)

    if colorbar:
        if colorbar == "nice":
            # make the colorbar closer to the plot and same size
            from mpl_toolkits.axes_grid1 import make_axes_locatable

            try:
                plt
            except NameError:
                import matplotlib.pyplot as plt
            try:
                gca_bkp = plt.gca()
                divider = make_axes_locatable(figure.gca())
                cax = divider.append_axes("right", size="5%", pad=0.05)
                _colorbar = figure.colorbar(patches, cax=cax)
                plt.sca(gca_bkp)
            except AttributeError as e:
                warn(e.args[0], RuntimeWarning)
        else:
            if not isinstance(colorbar, dict):
                colorbar = {}
            try:
                _colorbar = figure.colorbar(patches, ax=axes, **colorbar)
            except AttributeError as e:
                warn(e.args[0], RuntimeWarning)
        if clabel:
            unit = clabel
        if unit:
            _colorbar.set_label(unit)

    if return_patches:
        return patches, np.asarray(ids)
    else:
        return obj
Example #4
0
def scalar_map_2d(cells,
                  values,
                  aspect=None,
                  clim=None,
                  figure=None,
                  axes=None,
                  linewidth=1,
                  delaunay=False,
                  colorbar=True,
                  alpha=None,
                  colormap=None,
                  unit=None,
                  clabel=None,
                  xlim=None,
                  ylim=None,
                  **kwargs):
    """
    Plot a 2D scalar map as a colourful image.

    Arguments:

        cells (CellStats or Distributed): spatial description of the cells

        values (pandas.DataFrame or numpy.ndarray): feature value at each cell,
            that will be represented as a colour

        aspect (str): passed to :func:`~matplotlib.axes.Axes.set_aspect`

        clim (2-element sequence): passed to :func:`~matplotlib.cm.ScalarMappable.set_clim`

        figure (matplotlib.figure.Figure): figure handle

        axes (matplotlib.axes.Axes): axes handle

        linewidth (int): cell border line width

        delaunay (bool or dict): overlay the Delaunay graph; if ``dict``, options are passed
            to :func:`~tramway.core.plot.mesh.plot_delaunay`

        colorbar (bool or str or dict): add a colour bar; if ``dict``, options are passed to
            :func:`~matplotlib.pyplot.colorbar`;
            setting colorbar to '*nice*' allows to produce a colorbar close to the figure
            of the same size as the figure

        unit/clabel (str): colorbar label, usually the unit of displayed feature

        alpha (float): alpha value of the cells

        colormap (str): colormap name; see also https://matplotlib.org/users/colormaps.html

        xlim (2-element sequence): lower and upper x-axis bounds

        ylim (2-element sequence): lower and upper y-axis bounds

    Extra keyword arguments are passed to :func:`~matplotlib.collections.PatchCollection`.

    """
    coords = None
    if isinstance(values, pd.DataFrame):
        if values.shape[1] != 1:
            coords = values[[col for col in 'xyzt' if col in values.columns]]
            values = values[[
                col for col in values.columns if col not in 'xyzt'
            ]]
            if values.shape[1] != 1:
                warn('multiple parameters available; mapping first one only',
                     UserWarning)
        values = values.iloc[:, 0]  # to Series
    #values = pd.to_numeric(values, errors='coerce')

    # parse Delaunay-related arguments
    if delaunay:
        if not isinstance(delaunay, dict):
            delaunay = {}
        if linewidth and 'linewidth' not in delaunay:
            delaunay['linewidth'] = linewidth

    # turn the cells into polygons
    polygons = []
    if isinstance(cells, Distributed):

        ix, xy, ok = zip(*[(i, c.center, bool(c)) for i, c in cells.items()])
        ix, xy, ok = np.array(ix), np.array(xy), np.array(ok)
        if not (coords is None or np.all(np.isclose(xy, coords))):  # debug
            print('coordinates mismatch')
            print('in map:')
            print(xy)
            print('in cells:')
            print(coords)
        rp = bb = voronoi = None
        for c in range(xy.shape[0]):
            if ok[c]:
                vertices, voronoi, bb, rp = cell_to_polygon(
                    c, xy, voronoi, bb, rp, True)
                polygons.append(Polygon(vertices, True))

    elif isinstance(cells, CellStats) and isinstance(cells.tessellation,
                                                     Voronoi):

        Av = cells.tessellation.vertex_adjacency.tocsr()
        xy = cells.tessellation.cell_centers
        ix = np.arange(xy.shape[0])
        try:
            ok = 0 < cells.location_count
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            print(traceback.format_exc())
            ok = np.ones(ix.size, dtype=bool)
        if cells.tessellation.cell_label is not None:
            ok = np.logical_and(ok, 0 < cells.tessellation.cell_label)
        map_defined = np.zeros_like(ok)
        map_defined[values.index] = True
        ok[np.logical_not(map_defined)] = False
        ok[ok] = np.logical_not(np.isnan(values.loc[ix[ok]].values))
        for i in ix[ok]:
            vs = cells.tessellation.cell_vertices[i].tolist()
            # order the vertices so that they draw a polygon
            v0 = v = vs[0]
            vs = set(vs)
            vertices = []
            #vvs = [] # debug
            while True:
                vertices.append(cells.tessellation.vertices[v])
                #vvs.append(v)
                vs.remove(v)
                if not vs:
                    break
                ws = set(Av.indices[Av.indptr[v]:Av.indptr[v + 1]]) & vs
                if not ws:
                    ws = set(Av.indices[Av.indptr[v0]:Av.indptr[v0 + 1]]) & vs
                    if ws:
                        vertices = vertices[::-1]
                    else:
                        #print((v, vs, vvs, [Av.indices[Av.indptr[v]:Av.indptr[v+1]] for v in vs]))
                        warn(
                            'cannot find a path that connects all the vertices of a cell',
                            RuntimeWarning)
                        break
                v = ws.pop()
            #
            if vertices:
                vertices = np.vstack(vertices)
                polygons.append(Polygon(vertices, True))
    else:
        _type = repr(type(cells))
        if _type.endswith("'>"):
            _type = _type.split("'")[1]
        try:
            _nested_type = repr(type(cells.tessellation))
            if _nested_type.endswith("'>"):
                _nested_type = _nested_type.split("'")[1]
            raise TypeError('wrong type for `cells`: {}<{}>'.format(
                _type, _nested_type))
        except AttributeError:
            raise TypeError('wrong type for `cells`: {}'.format(_type))

    scalar_map = values.loc[ix[ok]].values

    #print(np.nonzero(~ok)[0])

    try:
        if np.any(np.isnan(scalar_map)):
            #print(np.nonzero(np.isnan(scalar_map)))
            msg = 'NaN found'
            try:
                warn(msg, NaNWarning)
            except:
                print('warning: {}'.format(msg))
            scalar_map[np.isnan(scalar_map)] = 0
    except TypeError:  # help debug
        print(scalar_map)
        print(scalar_map.dtype)
        raise

    if figure is None:
        import matplotlib.pyplot as plt
        figure = plt.gcf()  # before PatchCollection
    if axes is None:
        axes = figure.gca()
    # draw patches
    patch_kwargs = kwargs
    if alpha is not False:
        if alpha is None:
            alpha = .9
        patch_kwargs['alpha'] = alpha
    if colormap is not None:
        cmap = patch_kwargs.get('cmap', None)
        if cmap is None:
            patch_kwargs['cmap'] = colormap
        elif colormap != cmap:
            warn(
                'both cmap and colormap arguments are passed with different values',
                RuntimeWarning)
    patches = PatchCollection(polygons, linewidth=linewidth, **patch_kwargs)
    patches.set_array(scalar_map)
    if clim is not None:
        patches.set_clim(clim)
    axes.add_collection(patches)

    obj = None
    if delaunay or isinstance(delaunay, dict):
        try:
            import tramway.plot.mesh as mesh
            obj = mesh.plot_delaunay(cells,
                                     centroid_style=None,
                                     axes=axes,
                                     **delaunay)
        except:
            import traceback
            traceback.print_exc()

    if not xlim or not ylim:
        xy_min, _, xy_max, _ = _bounding_box(cells, xy)
        if not xlim:
            xlim = (xy_min[0], xy_max[0])
        if not ylim:
            ylim = (xy_min[1], xy_max[1])
    axes.set_xlim(*xlim)
    axes.set_ylim(*ylim)
    if aspect is not None:
        axes.set_aspect(aspect)

    if colorbar:
        if colorbar == 'nice':
            # make the colorbar closer to the plot and same size
            from mpl_toolkits.axes_grid1 import make_axes_locatable
            try:
                plt
            except NameError:
                import matplotlib.pyplot as plt
            try:
                gca_bkp = plt.gca()
                divider = make_axes_locatable(figure.gca())
                cax = divider.append_axes("right", size="5%", pad=0.05)
                _colorbar = figure.colorbar(patches, cax=cax)
                plt.sca(gca_bkp)
            except AttributeError as e:
                warn(e.args[0], RuntimeWarning)
        else:
            if not isinstance(colorbar, dict):
                colorbar = {}
            try:
                _colorbar = figure.colorbar(patches, ax=axes, **colorbar)
            except AttributeError as e:
                warn(e.args[0], RuntimeWarning)
        if clabel:
            unit = clabel
        if unit:
            _colorbar.set_label(unit)

    return obj