def _process(self, element, key=None): try: from matplotlib.contour import QuadContourSet from matplotlib.axes import Axes from matplotlib.figure import Figure except ImportError: raise ImportError("contours operation requires matplotlib.") extent = element.range(0) + element.range(1)[::-1] if type(element) is Raster: data = [np.flipud(element.data)] elif isinstance(element, Image): data = [np.flipud(element.dimension_values(2, flat=False))] elif isinstance(element, QuadMesh): data = (element.dimension_values(0, False), element.dimension_values(1, False), element.data[2]) if isinstance(self.p.levels, int): levels = self.p.levels + 2 if self.p.filled else self.p.levels + 3 zmin, zmax = element.range(2) levels = np.linspace(zmin, zmax, levels) else: levels = self.p.levels xdim, ydim = element.dimensions('key', label=True) fig = Figure() ax = Axes(fig, [0, 0, 1, 1]) contour_set = QuadContourSet(ax, *data, filled=self.p.filled, extent=extent, levels=levels) if self.p.filled: contour_type = Polygons else: contour_type = Contours vdims = element.vdims[:1] paths = [] empty = np.full((1, 2), np.NaN) for level, cset in zip(contour_set.get_array(), contour_set.collections): subpaths = [] for path in cset.get_paths(): if path.codes is None: subpaths.append(path.vertices) else: subpaths += np.split(path.vertices, np.where(path.codes == 1)[0][1:]) subpath = np.concatenate( [p for sp in subpaths for p in (sp, empty)][:-1]) paths.append({(xdim, ydim): subpath, element.vdims[0].name: level}) contours = contour_type(paths, label=element.label, kdims=element.kdims, vdims=vdims) if self.p.overlaid: contours = element * contours return contours
def _process(self, element, key=None): try: from matplotlib.contour import QuadContourSet from matplotlib.axes import Axes from matplotlib.figure import Figure from matplotlib.dates import num2date, date2num except ImportError: raise ImportError("contours operation requires matplotlib.") extent = element.range(0) + element.range(1)[::-1] xs = element.dimension_values(0, True, flat=False) ys = element.dimension_values(1, True, flat=False) zs = element.dimension_values(2, flat=False) # Ensure that coordinate arrays specify bin centers if xs.shape[0] != zs.shape[0]: xs = xs[:-1] + np.diff(xs, axis=0)/2. if xs.shape[1] != zs.shape[1]: xs = xs[:, :-1] + (np.diff(xs, axis=1)/2.) if ys.shape[0] != zs.shape[0]: ys = ys[:-1] + np.diff(ys, axis=0)/2. if ys.shape[1] != zs.shape[1]: ys = ys[:, :-1] + (np.diff(ys, axis=1)/2.) data = (xs, ys, zs) # if any data is a datetime, transform to matplotlib's numerical format data_is_datetime = tuple(isdatetime(arr) for k, arr in enumerate(data)) if any(data_is_datetime): data = tuple( date2num(d) if is_datetime else d for d, is_datetime in zip(data, data_is_datetime) ) xdim, ydim = element.dimensions('key', label=True) if self.p.filled: contour_type = Polygons else: contour_type = Contours vdims = element.vdims[:1] kwargs = {} levels = self.p.levels zmin, zmax = element.range(2) if isinstance(self.p.levels, int): if zmin == zmax: contours = contour_type([], [xdim, ydim], vdims) return (element * contours) if self.p.overlaid else contours data += (levels,) else: kwargs = {'levels': levels} fig = Figure() ax = Axes(fig, [0, 0, 1, 1]) contour_set = QuadContourSet(ax, *data, filled=self.p.filled, extent=extent, **kwargs) levels = np.array(contour_set.get_array()) crange = levels.min(), levels.max() if self.p.filled: levels = levels[:-1] + np.diff(levels)/2. vdims = [vdims[0].clone(range=crange)] paths = [] empty = np.array([[np.nan, np.nan]]) for level, cset in zip(levels, contour_set.collections): exteriors = [] interiors = [] for geom in cset.get_paths(): interior = [] polys = geom.to_polygons(closed_only=False) for ncp, cp in enumerate(polys): if any(data_is_datetime[0:2]): # transform x/y coordinates back to datetimes xs, ys = np.split(cp, 2, axis=1) if data_is_datetime[0]: xs = np.array(num2date(xs)) if data_is_datetime[1]: ys = np.array(num2date(ys)) cp = np.concatenate((xs, ys), axis=1) if ncp == 0: exteriors.append(cp) exteriors.append(empty) else: interior.append(cp) if len(polys): interiors.append(interior) if not exteriors: continue geom = { element.vdims[0].name: num2date(level) if data_is_datetime[2] else level, (xdim, ydim): np.concatenate(exteriors[:-1]) } if self.p.filled and interiors: geom['holes'] = interiors paths.append(geom) contours = contour_type(paths, label=element.label, kdims=element.kdims, vdims=vdims) if self.p.overlaid: contours = element * contours return contours
def _process(self, element, key=None): try: from matplotlib.contour import QuadContourSet from matplotlib.axes import Axes from matplotlib.figure import Figure except ImportError: raise ImportError("contours operation requires matplotlib.") extent = element.range(0) + element.range(1)[::-1] xs = element.dimension_values(0, True, flat=False) ys = element.dimension_values(1, True, flat=False) zs = element.dimension_values(2, flat=False) # Ensure that coordinate arrays specify bin centers if xs.shape[0] != zs.shape[0]: xs = xs[:-1] + np.diff(xs, axis=0)/2. if xs.shape[1] != zs.shape[1]: xs = xs[:, :-1] + (np.diff(xs, axis=1)/2.) if ys.shape[0] != zs.shape[0]: ys = ys[:-1] + np.diff(ys, axis=0)/2. if ys.shape[1] != zs.shape[1]: ys = ys[:, :-1] + (np.diff(ys, axis=1)/2.) data = (xs, ys, zs) xdim, ydim = element.dimensions('key', label=True) if self.p.filled: contour_type = Polygons else: contour_type = Contours vdims = element.vdims[:1] kwargs = {} levels = self.p.levels zmin, zmax = element.range(2) if isinstance(self.p.levels, int): if zmin == zmax: contours = contour_type([], [xdim, ydim], vdims) return (element * contours) if self.p.overlaid else contours data += (levels,) else: kwargs = {'levels': levels} fig = Figure() ax = Axes(fig, [0, 0, 1, 1]) contour_set = QuadContourSet(ax, *data, filled=self.p.filled, extent=extent, **kwargs) levels = np.array(contour_set.get_array()) crange = levels.min(), levels.max() if self.p.filled: levels = levels[:-1] + np.diff(levels)/2. vdims = [vdims[0].clone(range=crange)] paths = [] empty = np.array([[np.nan, np.nan]]) for level, cset in zip(levels, contour_set.collections): exteriors = [] interiors = [] for geom in cset.get_paths(): interior = [] polys = geom.to_polygons(closed_only=False) for ncp, cp in enumerate(polys): if ncp == 0: exteriors.append(cp) exteriors.append(empty) else: interior.append(cp) if len(polys): interiors.append(interior) if not exteriors: continue geom = {element.vdims[0].name: level, (xdim, ydim): np.concatenate(exteriors[:-1])} if self.p.filled and interiors: geom['holes'] = interiors paths.append(geom) contours = contour_type(paths, label=element.label, kdims=element.kdims, vdims=vdims) if self.p.overlaid: contours = element * contours return contours