def set_bbox_to_anchor(self, bbox, transform=None): """ set the bbox that the legend will be anchored. *bbox* can be a BboxBase instance, a tuple of [left, bottom, width, height] in the given transform (normalized axes coordinate if None), or a tuple of [left, bottom] where the width and height will be assumed to be zero. """ if bbox is None: self._bbox_to_anchor = None return elif isinstance(bbox, BboxBase): self._bbox_to_anchor = bbox else: try: l = len(bbox) except TypeError: raise ValueError("Invalid argument for bbox : %s" % str(bbox)) if l == 2: bbox = [bbox[0], bbox[1], 0, 0] self._bbox_to_anchor = Bbox.from_bounds(*bbox) if transform is None: transform = BboxTransformTo(self.parent.bbox) self._bbox_to_anchor = TransformedBbox(self._bbox_to_anchor, transform)
def _set_lim_and_transforms(self): self.transAxes = BboxTransformTo(self.bbox) # Transforms the x and y axis separately by a scale factor # It is assumed that this part will have non-linear components self.transScale = TransformWrapper(IdentityTransform()) # A (possibly non-linear) projection on the (already scaled) # data. This one is aware of rmin self.transProjection = self.PolarTransform(self) # This one is not aware of rmin self.transPureProjection = self.PolarTransform() # An affine transformation on the data, generally to limit the # range of the axes self.transProjectionAffine = self.PolarAffine(self.transScale, self.viewLim) # The complete data transformation stack -- from data all the # way to display coordinates self.transData = self.transScale + self.transProjection + \ (self.transProjectionAffine + self.transAxes) # This is the transform for theta-axis ticks. It is # equivalent to transData, except it always puts r == 1.0 at # the edge of the axis circle. self._xaxis_transform = ( self.transPureProjection + self.PolarAffine(IdentityTransform(), Bbox.unit()) + self.transAxes) # The theta labels are moved from radius == 0.0 to radius == 1.1 self._theta_label1_position = Affine2D().translate(0.0, 1.1) self._xaxis_text1_transform = ( self._theta_label1_position + self._xaxis_transform) self._theta_label2_position = Affine2D().translate(0.0, 1.0 / 1.1) self._xaxis_text2_transform = ( self._theta_label2_position + self._xaxis_transform) # This is the transform for r-axis ticks. It scales the theta # axis so the gridlines from 0.0 to 1.0, now go from 0.0 to # 2pi. self._yaxis_transform = ( Affine2D().scale(np.pi * 2.0, 1.0) + self.transData) # The r-axis labels are put at an angle and padded in the r-direction self._r_label1_position = Affine2D().translate(22.5, self._rpad) self._yaxis_text1_transform = ( self._r_label1_position + Affine2D().scale(1.0 / 360.0, 1.0) + self._yaxis_transform ) self._r_label2_position = Affine2D().translate(22.5, self._rpad) self._yaxis_text2_transform = ( self._r_label2_position + Affine2D().scale(1.0 / 360.0, 1.0) + self._yaxis_transform )
def _set_lim_and_transforms(self): self.transProjection = StereographicTransform(pole=self.pole) self.transAffine = StereographicAffine(pole=self.pole) self.transAxes = BboxTransformTo(self.bbox) self.transData = self.transProjection + self.transAffine + self.transAxes self._xaxis_pretransform = Affine2D().scale(1, self._polar_cap) self._xaxis_transform = self._xaxis_pretransform + self.transData self._yaxis_pretransform = Affine2D().scale(self._azimuth_cap, 1) self._yaxis_transform = self._yaxis_pretransform + self.transData
def compute_lims(self): '''compute new axes limits based on pan/zoom. basically, get limits in terms of bbox of size 1. transform to bbox of size orig lims. ''' xlnorm = self.hslider.barvalue + np.array([-.5, .5 ]) * self.hslider.barwidth ylnorm = self.vslider.barvalue + np.array([-.5, .5 ]) * self.vslider.barwidth viewBbox = Bbox(zip(xlnorm, ylnorm)) limBB = Bbox(zip(self.xlim, self.ylim)) newlims = Bbox(BboxTransformTo(limBB).transform(viewBbox)) return [(newlims.x0, newlims.x1), (newlims.y0, newlims.y1)]
def _set_lim_and_transforms(self): # A (possibly non-linear) projection on the (already scaled) data self.transProjection = self._get_core_transform(self.RESOLUTION) self.transAffine = self._get_affine_transform() self.transAxes = BboxTransformTo(self.bbox) # The complete data transformation stack -- from data all the # way to display coordinates self.transData = \ self.transProjection + \ self.transAffine + \ self.transAxes # This is the transform for longitude ticks. self._xaxis_pretransform = \ Affine2D() \ .scale(1.0, self._longitude_cap * 2.0) \ .translate(0.0, -self._longitude_cap) self._xaxis_transform = \ self._xaxis_pretransform + \ self.transData self._xaxis_text1_transform = \ Affine2D().scale(1.0, 0.0) + \ self.transData + \ Affine2D().translate(0.0, 4.0) self._xaxis_text2_transform = \ Affine2D().scale(1.0, 0.0) + \ self.transData + \ Affine2D().translate(0.0, -4.0) # This is the transform for latitude ticks. yaxis_stretch = Affine2D().scale(np.pi * 2.0, 1.0).translate(-np.pi, 0.0) yaxis_space = Affine2D().scale(1.0, 1.1) self._yaxis_transform = \ yaxis_stretch + \ self.transData yaxis_text_base = \ yaxis_stretch + \ self.transProjection + \ (yaxis_space + \ self.transAffine + \ self.transAxes) self._yaxis_text1_transform = \ yaxis_text_base + \ Affine2D().translate(-8.0, 0.0) self._yaxis_text2_transform = \ yaxis_text_base + \ Affine2D().translate(8.0, 0.0)
def _set_lim_and_transforms(self): """ This is called once when the plot is created to set up all the transforms for the data, text and grids. """ # This code is based off of matplotlib's example for a custom Hammer # projection. See: https://matplotlib.org/gallery/misc/custom_projection.html#sphx-glr-gallery-misc-custom-projection-py # This function makes heavy use of the Transform classes in # ``lib/matplotlib/transforms.py.`` For more information, see # the inline documentation there. # Affine2D.from_values(a, b, c, d, e, f) constructs an affine # transformation matrix of # a c e # b d f # 0 0 1 # A useful reference for the different coordinate systems can be found # in a table in the matplotlib transforms tutorial: # https://matplotlib.org/tutorials/advanced/transforms_tutorial.html#transformations-tutorial # The goal of this transformation is to get from the data space to axes # space. We perform an affine transformation on the y-axis, i.e. # transforming the y-axis from (0, 1) to (0.5, sqrt(3)/2). self.transAffine = Affine2D.from_values(1., 0, 0.5, np.sqrt(3) / 2., 0, 0) # Affine transformation along the dependent axis self.transAffinedep = Affine2D.from_values(1., 0, -0.5, np.sqrt(3) / 2., 0, 0) # This is the transformation from axes space to display space. self.transAxes = BboxTransformTo(self.bbox) # The data transformation is the application of the affine # transformation from data to axes space, then from axes to display # space. The '+' operator applies these in order. self.transData = self.transAffine + self.transAxes # The main data transformation is set up. Now deal with gridlines and # tick labels. For these, we want the same trasnform as the, so we # apply transData directly. self._xaxis_transform = self.transData self._xaxis_text1_transform = self.transData self._xaxis_text2_transform = self.transData self._yaxis_transform = self.transData self._yaxis_text1_transform = self.transData self._yaxis_text2_transform = self.transData
def convert_point(self, point_px, stop=False): ''' given a touch point in the view space, compute the corresponding point in data coords. assumes linear scaling! TODO: support log scaling there are basically two bbox transforms: 1) from figure coords to view coords, accounting for sign change in y. this then lets us compute axes box in view coords, and generate 2) transform from view to data coords. ''' transFig = BboxTransformTo(Bbox([(0, self.height), (self.width, 0)])) bbox_axes = Bbox(transFig.transform(plt.gca().get_position())) bbox_data = Bbox([(self.xlim[0], self.ylim[0]), (self.xlim[1], self.ylim[1])]) transMPL = BboxTransform(bbox_axes, bbox_data) self.trans = transMPL ax_pt = transMPL.transform_point(point_px) return ax_pt
def _set_lim_and_transforms(self): """ This is called once when the plot is created to set up all the transforms for the data, text and grids. """ # There are three important coordinate spaces going on here: # # 1. Data space: The space of the data itself # # 2. Axes space: The unit rectangle (0, 0) to (1, 1) # covering the entire plot area. # # 3. Display space: The coordinates of the resulting image, # often in pixels or dpi/inch. # This function makes heavy use of the Transform classes in # ``lib/matplotlib/transforms.py.`` For more information, see # the inline documentation there. # The goal of the first two transformations is to get from the # data space to axes space. It is separated into a non-affine # and affine part so that the non-affine part does not have to be # recomputed when a simple affine change to the figure has been # made (such as resizing the window or changing the dpi). # 3) This is the transformation from axes space to display # space. self.transAxes = BboxTransformTo(self.bbox) # Now put these 3 transforms together -- from data all the way # to display coordinates. Using the '+' operator, these # transforms will be applied "in order". The transforms are # automatically simplified, if possible, by the underlying # transformation framework. #self.transData = \ # self.transProjection + self.transAffine + self.transAxes self.transData = self.GingaTransform() self.transData.viewer = self.viewer # self._xaxis_transform = blended_transform_factory( # self.transData, self.transAxes) # self._yaxis_transform = blended_transform_factory( # self.transAxes, self.transData) self._xaxis_transform = self.transData self._yaxis_transform = self.transData
def _set_lim_and_transforms(self): self.transProjection = self._get_core_transform(self.RESOLUTION) self.transAffine = self._get_affine_transform() self.transAxes = BboxTransformTo(self.bbox) self.transData = \ self.transProjection + \ self.transAffine + \ self.transAxes self._xaxis_pretransform = \ Affine2D() \ .scale(1.0, self._longitude_cap * 2.0)\ .translate(0.0, -self._longitude_cap) self._xaxis_transform = \ self._xaxis_pretransform + \ self.transData self._xaxis_text1_transform = \ Affine2D().scale(1.0, 0.0) + \ self.transData + \ Affine2D().translate(0.0, 4.0) self._xaxis_text2_transform = \ Affine2D().scale(1.0, 0.0) + \ self.transData + \ Affine2D().translate(0.0, -4.0) yaxis_stretch = Affine2D().scale(np.pi * 2, 1).translate(-np.pi, 0) yaxis_space = Affine2D().scale(1.0, 1.1) self._yaxis_transform = \ yaxis_stretch + \ self.transData yaxis_text_base = \ yaxis_stretch + \ self.transProjection + \ (yaxis_space + self.transAffine + self.transAxes) self._yaxis_text1_transform = \ yaxis_text_base + \ Affine2D().translate(-8.0, 0.0) self._yaxis_text2_transform = \ yaxis_text_base + \ Affine2D().translate(8.0, 0.0)
def set_bbox_to_anchor(self, bbox, transform=None): """ Set the bbox that the legend will be anchored to. Parameters ---------- bbox : `~matplotlib.transforms.BboxBase` or tuple The bounding box can be specified in the following ways: - A `.BboxBase` instance - A tuple of ``(left, bottom, width, height)`` in the given transform (normalized axes coordinate if None) - A tuple of ``(left, bottom)`` where the width and height will be assumed to be zero. - *None*, to remove the bbox anchoring, and use the parent bbox. transform : `~matplotlib.transforms.Transform`, optional A transform to apply to the bounding box. If not specified, this will use a transform to the bounding box of the parent. """ if bbox is None: self._bbox_to_anchor = None return elif isinstance(bbox, BboxBase): self._bbox_to_anchor = bbox else: try: l = len(bbox) except TypeError as err: raise ValueError("Invalid argument for bbox : %s" % str(bbox)) from err if l == 2: bbox = [bbox[0], bbox[1], 0, 0] self._bbox_to_anchor = Bbox.from_bounds(*bbox) if transform is None: transform = BboxTransformTo(self.parent.bbox) self._bbox_to_anchor = TransformedBbox(self._bbox_to_anchor, transform) self.stale = True
def create_artists(self, legend, orig_handle, xdescent, ydescent, width, height, fontsize, trans): legline, _ = HandlerLine2D.create_artists(self, legend, orig_handle, xdescent, ydescent, width, height, fontsize, trans) legline.set_data(*orig_handle.get_data()) ext = mpath.get_paths_extents([orig_handle.get_path()]) if ext.width == 0: ext.x0 -= 0.1 ext.x1 += 0.1 bbox0 = BboxTransformFrom(ext) bbox1 = BboxTransformTo( Bbox.from_bounds(xdescent, ydescent, width, height)) legline.set_transform(bbox0 + bbox1 + trans) return legline,
def _set_lim_and_transforms(self): # A (possibly non-linear) projection on the (already scaled) data # There are three important coordinate spaces going on here: # # 1. Data space: The space of the data itself # # 2. Axes space: The unit rectangle (0, 0) to (1, 1) # covering the entire plot area. # # 3. Display space: The coordinates of the resulting image, # often in pixels or dpi/inch. # This function makes heavy use of the Transform classes in # ``lib/matplotlib/transforms.py.`` For more information, see # the inline documentation there. # The goal of the first two transformations is to get from the # data space (in this case longitude and latitude) to axes # space. It is separated into a non-affine and affine part so # that the non-affine part does not have to be recomputed when # a simple affine change to the figure has been made (such as # resizing the window or changing the dpi). # 1) The core transformation from data space into # rectilinear space defined in the HammerTransform class. self.transProjection = self._get_core_transform(self.RESOLUTION) # 2) The above has an output range that is not in the unit # rectangle, so scale and translate it so it fits correctly # within the axes. The peculiar calculations of xscale and # yscale are specific to a Aitoff-Hammer projection, so don't # worry about them too much. self.transAffine = self._get_affine_transform() # 3) This is the transformation from axes space to display # space. self.transAxes = BboxTransformTo(self.bbox) # Now put these 3 transforms together -- from data all the way # to display coordinates. Using the '+' operator, these # transforms will be applied "in order". The transforms are # automatically simplified, if possible, by the underlying # transformation framework. self.transData = \ self.transProjection + \ self.transAffine + \ self.transAxes # The main data transformation is set up. Now deal with # gridlines and tick labels. # Longitude gridlines and ticklabels. The input to these # transforms are in display space in x and axes space in y. # Therefore, the input values will be in range (-xmin, 0), # (xmax, 1). The goal of these transforms is to go from that # space to display space. The tick labels will be offset 4 # pixels from the equator. self._xaxis_pretransform = \ Affine2D() \ .scale(1.0, self._longitude_cap * 2.0) \ .translate(0.0, -self._longitude_cap) self._xaxis_transform = \ self._xaxis_pretransform + \ self.transData self._xaxis_text1_transform = \ Affine2D().scale(1.0, 0.0) + \ self.transData + \ Affine2D().translate(0.0, 4.0) self._xaxis_text2_transform = \ Affine2D().scale(1.0, 0.0) + \ self.transData + \ Affine2D().translate(0.0, -4.0) # Now set up the transforms for the latitude ticks. The input to # these transforms are in axes space in x and display space in # y. Therefore, the input values will be in range (0, -ymin), # (1, ymax). The goal of these transforms is to go from that # space to display space. The tick labels will be offset 4 # pixels from the edge of the axes ellipse. yaxis_stretch = Affine2D().scale(np.pi * 2, 1).translate(-np.pi, 0) yaxis_space = Affine2D().scale(1.0, 1.1) self._yaxis_transform = \ yaxis_stretch + \ self.transData yaxis_text_base = \ yaxis_stretch + \ self.transProjection + \ (yaxis_space + self.transAffine + self.transAxes) self._yaxis_text1_transform = \ yaxis_text_base + \ Affine2D().translate(-8.0, 0.0) self._yaxis_text2_transform = \ yaxis_text_base + \ Affine2D().translate(8.0, 0.0)
def _set_lim_and_transforms(self): """ Hanya akan berlangsung sekali saja ketika plot/alur sudah menentukan pengaturan umum pada transformasi pada, teks dan tempat """ # Ada beberapa bagian yang harus anda ketahui : #1. Data space : ruang pada data itu sendiri #2. Axes space : unit segiempat (0, 0) to (1, 1) untuk # melengkapi area masukan #3. Display space : kordinat yang dihasilkan pada gambar # biasanya dalam pixel atau dpi/inch # #fungsi ini lumayan berat pada transformasi kelas dalam # ``lib/matplotlib/transforms.py.`` #tujuan dari transformasi awal kedua transformasi adalah menetapkan #data space dalam hal ini adalah longitude dan latitude pada ruang sumbu #hal ini kemudian dibagi menjadi non-affine dan affine yang dimaksud dengan #non-affine adalah dimana tidak ada perhitungan kembali ketika #affine sudah dirubah menjadi gambar yang sudah jadi seperti #merubah ukuran tampilan dan mengubah dpi # # 1) Inti transfrmasi dari data space menjadi rectilinear space #sudah dijelaskan dalam kelas HammerTransform self.transProjection = self.HammerTransform() # 2) Rentang hasil keluaran sebelumnya bukan dalam segiempat unit #maka skala dan juga penerjemahannya menjadi cocok dengan sumbu #perhitungan yang unik pada xscale dan yscale spesifik pada #projek Aitoff-Hammer maka tidak usah ragu xscale = 2.0 * np.sqrt(2.0) * np.sin(0.5 * np.pi) yscale = np.sqrt(2.0) * np.sin(0.5 * np.pi) self.transAffine = Affine2D() \ .scale(0.5 / xscale, 0.5 / yscale) \ .translate(0.5, 0.5) # 3) Menstransformasikan dari ruang sumbu pada ruang tampilan self.transAxes = BboxTransformTo(self.bbox) #tambahkan ke 3 transormasi ini bersama-sama dari seluruh data #untuk mencapai tampilan kordinat menggunakan operator '+' , #ditransformasi menjadi "in order". Transformasi secara otomatis #sangatlah sederhana, kalaupun bisa dengan dasar framework transformasi self.transData = \ self.transProjection + \ self.transAffine + \ self.transAxes #data utama transformasi sudah ditentukan #maka sekarang tentukan gridlines and tick labels. #Longitude gridlines dan ticklabels. Masukan pada transformasi #dalam display space x and sumbu ruang y. #Maka, nilai masukan dalam rentang(-xmin, 0), (xmax, 1) #Tujuan akhir dari transformasi ini adalah mencapai ruang display space #ketebalan label akan menjadi offset 4 pixels dari the equator self._xaxis_pretransform = \ Affine2D() \ .scale(1.0, np.pi) \ .translate(0.0, -np.pi) self._xaxis_transform = \ self._xaxis_pretransform + \ self.transData self._xaxis_text1_transform = \ Affine2D().scale(1.0, 0.0) + \ self.transData + \ Affine2D().translate(0.0, 4.0) self._xaxis_text2_transform = \ Affine2D().scale(1.0, 0.0) + \ self.transData + \ Affine2D().translate(0.0, -4.0) #menentukan ketebalan latitude. Masukan pada transformasi dalam #sumbu ruang pada x dan display space pada y. Selanjutnya, #masukan pada transformasidalam display space x and sumbu ruang y. #Maka, nilai masukan dalam rentang(-xmin, 0), (xmax, 1) #Tujuan akhir dari transformasi ini adalah mencapai ruang display space #ketebalan label akan menjadi offset 4 pixels dari the equator yaxis_stretch = Affine2D().scale(np.pi * 2.0, 1.0).translate(-np.pi, 0.0) yaxis_space = Affine2D().scale(1.0, 1.1) self._yaxis_transform = \ yaxis_stretch + \ self.transData yaxis_text_base = \ yaxis_stretch + \ self.transProjection + \ (yaxis_space + \ self.transAffine + \ self.transAxes) self._yaxis_text1_transform = \ yaxis_text_base + \ Affine2D().translate(-8.0, 0.0) self._yaxis_text2_transform = \ yaxis_text_base + \ Affine2D().translate(8.0, 0.0)
numSamples, numRows = 800, 4 data = fromstring(file('data/eeg.dat', 'rb').read(), float) data.shape = numSamples, numRows t = arange(numSamples) / float(numSamples) * 10.0 ticklocs = [] ax = subplot(212) xlim(0, 10) xticks(arange(10)) boxin = Bbox.from_extents(ax.viewLim.x0, -20, ax.viewLim.x1, 20) height = ax.bbox.height boxout = Bbox.from_extents(ax.bbox.x0, -1.0 * height, ax.bbox.x1, 1.0 * height) transOffset = BboxTransformTo( Bbox.from_extents(0.0, ax.bbox.y0, 1.0, ax.bbox.y1)) for i in range(numRows): # effectively a copy of transData trans = BboxTransform(boxin, boxout) offset = (i + 1) / (numRows + 1) trans += Affine2D().translate(*transOffset.transform_point((0, offset))) thisLine = Line2D( t, data[:, i] - data[0, i], ) thisLine.set_transform(trans)
def __call__(self, ax, renderer): bbox_parent = self.parent.get_position(original=False) trans = BboxTransformTo(bbox_parent) bbox_inset = Bbox.from_bounds(*self.lbwh) bb = TransformedBbox(bbox_inset, trans) return bb
def _set_lim_and_transforms(self): """ This is called once when the plot is created to set up all the transforms for the data, text and grids. """ # There are three important coordinate spaces going on here: # # 1. Data space: The space of the data itself # # 2. Axes space: The unit rectangle (0, 0) to (1, 1) # covering the entire plot area. # # 3. Display space: The coordinates of the resulting image, # often in pixels or dpi/inch. # This function makes heavy use of the Transform classes in # ``lib/matplotlib/transforms.py.`` For more information, see # the inline documentation there. # The goal of the first two transformations is to get from the # data space (in this case meridian and parallel) to axes # space. It is separated into a non-affine and affine part so # that the non-affine part does not have to be recomputed when # a simple affine change to the figure has been made (such as # resizing the window or changing the dpi). # 1) The core transformation from data space into # rectilinear space defined in the HammerTransform class. self.transProjection = self.get_projection_class()() self.transProjection.set_center((180, 0)) self.transProjection.set_dec1(-65) self.transProjection.set_dec2(80) # 2) The above has an output range that is not in the unit # rectangle, so scale and translate it so it fits correctly # within the axes. The peculiar calculations of xscale and # yscale are specific to a Aitoff-Hammer projection, so don't # worry about them too much. # This will be updated after the xy limits are set. self.transAffine = Affine2D() # 3) This is the transformation from axes space to display # space. self.transAxes = BboxTransformTo(self.bbox) # Now put these 3 transforms together -- from data all the way # to display coordinates. Using the '+' operator, these # transforms will be applied "in order". The transforms are # automatically simplified, if possible, by the underlying # transformation framework. self.transData = \ self.transProjection + \ self.transAffine + \ self.transAxes self.transClip = \ self.transProjection + \ self.transAffine + \ self.transAxes # The main data transformation is set up. Now deal with # gridlines and tick labels. # Longitude gridlines and ticklabels. The input to these # transforms are in display space in x and axes space in y. # Therefore, the input values will be in range (-xmin, 0), # (xmax, 1). The goal of these transforms is to go from that # space to display space. The tick labels will be offset 4 # pixels from the equator. self._xaxis_pretransform = \ Affine2D() \ .scale(1.0, 180) \ .translate(0.0, -90) self._xaxis_transform = \ self._xaxis_pretransform + \ self.transData self._xaxis_text1_transform = \ self._xaxis_pretransform + \ self.transData# + \ # Affine2D().translate(0.0, -8.0) self._xaxis_text2_transform = \ self._xaxis_pretransform+ \ self.transData# + \ # Affine2D().translate(0.0, -8.0) # Now set up the transforms for the parallel ticks. The input to # these transforms are in axes space in x and display space in # y. Therefore, the input values will be in range (0, -ymin), # (1, ymax). The goal of these transforms is to go from that # space to display space. The tick labels will be offset 4 # pixels from the edge of the axes ellipse. self._yaxis_stretch = Affine2D().scale(360, 1.0).translate(0.0, 0.0) self._yaxis_stretch1 = Affine2D().scale(360, 1.0).translate(0.0, 0.0) self._yaxis_stretch2 = Affine2D().scale(360, 1.0).translate(0.0, 0.0) self._yaxis_transform = \ self._yaxis_stretch + \ self.transData self._yaxis_text1_transform = \ self._yaxis_stretch1 + \ self.transData# + \ # Affine2D().translate(-8.0, 0.0) self._yaxis_text2_transform = \ self._yaxis_stretch2 + \ self.transData# + \
def __init__(self, ax, bbox, color, *, extent=(0, 1, 0, 1), **kwargs): super().__init__(ax, extent=extent, **kwargs) self._bbox = bbox self._ribbonbox = RibbonBox(color) self.set_transform(BboxTransformTo(bbox))