:py:mod:`kwimage.structs.polygon` ================================= .. py:module:: kwimage.structs.polygon Module Contents --------------- Classes ~~~~~~~ .. autoapisummary:: kwimage.structs.polygon._PolyArrayBackend kwimage.structs.polygon._PolyWarpMixin kwimage.structs.polygon.Polygon kwimage.structs.polygon.MultiPolygon kwimage.structs.polygon.PolygonList Functions ~~~~~~~~~ .. autoapisummary:: kwimage.structs.polygon._is_clockwise kwimage.structs.polygon._order_vertices .. py:class:: _PolyArrayBackend .. py:method:: is_numpy(self) .. py:method:: is_tensor(self) .. py:method:: tensor(self, device=ub.NoParam) .. rubric:: Example >>> # xdoctest: +REQUIRES(module:torch) >>> from kwimage.structs.polygon import * >>> self = Polygon.random() >>> self.tensor() .. py:method:: numpy(self) .. rubric:: Example >>> # xdoctest: +REQUIRES(module:torch) >>> from kwimage.structs.polygon import * >>> self = Polygon.random() >>> self.tensor().numpy().tensor().numpy() .. py:class:: _PolyWarpMixin .. py:method:: _warp_imgaug(self, augmenter, input_dims, inplace=False) Warps by applying an augmenter from the imgaug library :Parameters: * **augmenter** (*imgaug.augmenters.Augmenter*) * **input_dims** (*Tuple*) -- h/w of the input image * **inplace** (*bool, default=False*) -- if True, modifies data inplace .. rubric:: Example >>> # xdoctest: +REQUIRES(module:imgaug) >>> from kwimage.structs.polygon import * # NOQA >>> import imgaug >>> input_dims = np.array((10, 10)) >>> self = Polygon.random(10, n_holes=1, rng=0).scale(input_dims) >>> augmenter = imgaug.augmenters.Fliplr(p=1) >>> new = self._warp_imgaug(augmenter, input_dims) >>> assert np.allclose(self.data['exterior'].data[:, 1], new.data['exterior'].data[:, 1]) >>> assert np.allclose(input_dims[0] - self.data['exterior'].data[:, 0], new.data['exterior'].data[:, 0]) >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> kwplot.figure(fnum=1, doclf=True) >>> from matplotlib import pyplot as pl >>> ax = plt.gca() >>> ax.set_xlim(0, 10) >>> ax.set_ylim(0, 10) >>> self.draw(color='red', alpha=.4) >>> new.draw(color='blue', alpha=.4) .. py:method:: to_imgaug(self, shape) .. py:method:: warp(self, transform, input_dims=None, output_dims=None, inplace=False) Generalized coordinate transform. :Parameters: * **transform** (*GeometricTransform | ArrayLike | Augmenter | callable*) -- scikit-image tranform, a 3x3 transformation matrix, an imgaug Augmenter, or generic callable which transforms an NxD ndarray. * **input_dims** (*Tuple*) -- shape of the image these objects correspond to (only needed / used when transform is an imgaug augmenter) * **output_dims** (*Tuple*) -- unused, only exists for compatibility * **inplace** (*bool, default=False*) -- if True, modifies data inplace .. rubric:: Example >>> from kwimage.structs.polygon import * # NOQA >>> self = Polygon.random() >>> transform = skimage.transform.AffineTransform(scale=(2, 2)) >>> new = self.warp(transform) Doctest: >>> # xdoctest: +REQUIRES(module:imgaug) >>> self = Polygon.random() >>> import imgaug >>> augmenter = imgaug.augmenters.Fliplr(p=1) >>> new = self.warp(augmenter, input_dims=(1, 1)) >>> print('new = {!r}'.format(new.data)) >>> print('self = {!r}'.format(self.data)) >>> #assert np.all(self.warp(np.eye(3)).exterior == self.exterior) >>> #assert np.all(self.warp(np.eye(2)).exterior == self.exterior) .. py:method:: scale(self, factor, about=None, output_dims=None, inplace=False) Scale a polygon by a factor :Parameters: * **factor** (*float or Tuple[float, float]*) -- scale factor as either a scalar or a (sf_x, sf_y) tuple. * **about** (*Tuple | None*) -- if unspecified scales about the origin (0, 0), otherwise the rotation is about this point. * **output_dims** (*Tuple*) -- unused in non-raster spatial structures * **inplace** (*bool, default=False*) -- if True, modifies data inplace .. rubric:: Example >>> from kwimage.structs.polygon import * # NOQA >>> self = Polygon.random(10, rng=0) >>> new = self.scale(10) .. rubric:: Example >>> from kwimage.structs.polygon import * # NOQA >>> self = Polygon.random(10, rng=0).translate((0.5)) >>> new = self.scale(1.5, about='center') >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.figure(fnum=1, doclf=True) >>> kwplot.autompl() >>> self.draw(color='red', alpha=0.5) >>> new.draw(color='blue', alpha=0.5, setlim=True) .. py:method:: translate(self, offset, output_dims=None, inplace=False) Shift the polygon up/down left/right :Parameters: * **factor** (*float or Tuple[float]*) -- transation amount as either a scalar or a (t_x, t_y) tuple. * **output_dims** (*Tuple*) -- unused in non-raster spatial structures * **inplace** (*bool, default=False*) -- if True, modifies data inplace .. rubric:: Example >>> from kwimage.structs.polygon import * # NOQA >>> self = Polygon.random(10, rng=0) >>> new = self.translate(10) .. py:method:: rotate(self, theta, about=None, output_dims=None, inplace=False) Rotate the polygon :Parameters: * **theta** (*float*) -- rotation angle in radians * **about** (*Tuple | None | str*) -- if unspecified rotates about the origin (0, 0). If "center" then rotate around the center of this polygon. Otherwise the rotation is about a custom specified point. * **output_dims** (*Tuple*) -- unused in non-raster spatial structures .. rubric:: Example >>> from kwimage.structs.polygon import * # NOQA >>> self = Polygon.random(10, rng=0) >>> new = self.rotate(np.pi / 2, about='center') >>> new2 = self.rotate(np.pi / 2) >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.figure(fnum=1, doclf=True) >>> kwplot.autompl() >>> self.draw(color='red', alpha=0.5) >>> new.draw(color='blue', alpha=0.5) .. py:method:: _rectify_about(self, about) Ensures that about returns a specified point. Allows for special keys like center to be used. .. rubric:: Example >>> from kwimage.structs.polygon import * # NOQA >>> self = Polygon.random(10, rng=0) >>> self._rectify_about('center') .. py:method:: swap_axes(self, inplace=False) Swap the x and y coordinate axes :Parameters: **inplace** (*bool, default=False*) -- if True, modifies data inplace :returns: modified polygon :rtype: Polygon .. py:class:: Polygon(data=None, meta=None, datakeys=None, metakeys=None, **kwargs) Bases: :py:obj:`kwimage.structs._generic.Spatial`, :py:obj:`_PolyArrayBackend`, :py:obj:`_PolyWarpMixin`, :py:obj:`ubelt.NiceRepr` Represents a single polygon as set of exterior boundary points and a list of internal polygons representing holes. By convention exterior boundaries should be counterclockwise and interior holes should be clockwise. .. rubric:: Example >>> import kwimage >>> data = { >>> 'exterior': np.array([[13, 1], [13, 19], [25, 19], [25, 1]]), >>> 'interiors': [ >>> np.array([[13, 13], [14, 12], [24, 12], [25, 13], [25, 18], >>> [24, 19], [14, 19], [13, 18]]), >>> np.array([[13, 2], [14, 1], [24, 1], [25, 2], [25, 11], >>> [24, 12], [14, 12], [13, 11]])] >>> } >>> self = kwimage.Polygon(**data) >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> self.draw(setlim=True) .. rubric:: Example >>> import kwimage >>> self = kwimage.Polygon.random( >>> n=5, n_holes=1, convex=False, rng=0) >>> print('self = {}'.format(self)) self = , 'interiors': [], })> >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> self.draw(setlim=True) .. py:attribute:: __datakeys__ :annotation: = ['exterior', 'interiors'] .. py:attribute:: __metakeys__ :annotation: = ['classes'] .. py:method:: exterior(self) :property: .. py:method:: interiors(self) :property: .. py:method:: __nice__(self) .. py:method:: circle(cls, xy, r, resolution=64) :classmethod: Create a circular polygon .. rubric:: Example >>> xy = (0.5, 0.5) >>> r = .3 >>> poly = Polygon.circle(xy, r) .. py:method:: random(cls, n=6, n_holes=0, convex=True, tight=False, rng=None) :classmethod: :Parameters: * **n** (*int*) -- number of points in the polygon (must be 3 or more) * **n_holes** (*int*) -- number of holes * **tight** (*bool, default=False*) -- fits the minimum and maximum points between 0 and 1 * **convex** (*bool, default=True*) -- force resulting polygon will be convex (may remove exterior points) CommandLine: xdoctest -m kwimage.structs.polygon Polygon.random .. rubric:: Example >>> rng = None >>> n = 4 >>> n_holes = 1 >>> cls = Polygon >>> self = Polygon.random(n=n, rng=rng, n_holes=n_holes, convex=1) >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.figure(fnum=1, doclf=True) >>> kwplot.autompl() >>> self.draw() .. rubric:: References https://gis.stackexchange.com/questions/207731/random-multipolygon https://stackoverflow.com/questions/8997099/random-polygon https://stackoverflow.com/questions/27548363/from-voronoi-tessellation-to-shapely-polygons https://stackoverflow.com/questions/8997099/algorithm-to-generate-random-2d-polygon .. py:method:: _impl(self) .. py:method:: to_mask(self, dims=None) Convert this polygon to a mask .. todo:: - [ ] currently not efficient :Parameters: **dims** (*Tuple*) -- height and width of the output mask :returns: kwimage.Mask .. rubric:: Example >>> from kwimage.structs.polygon import * # NOQA >>> self = Polygon.random(n_holes=1).scale(128) >>> mask = self.to_mask((128, 128)) >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> kwplot.figure(fnum=1, doclf=True) >>> mask.draw(color='blue') >>> mask.to_multi_polygon().draw(color='red', alpha=.5) .. py:method:: to_relative_mask(self) Returns a translated mask such the mask dimensions are minimal. In other words, we move the polygon all the way to the top-left and return a mask just big enough to fit the polygon. :returns: kwimage.Mask .. rubric:: Example >>> from kwimage.structs.polygon import * # NOQA >>> self = Polygon.random().scale(8).translate(100, 100) >>> mask = self.to_relative_mask() >>> assert mask.shape <= (8, 8) >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> kwplot.figure(fnum=1, doclf=True) >>> mask.draw(color='blue') >>> mask.to_multi_polygon().draw(color='red', alpha=.5) .. py:method:: fill(self, image, value=1) Inplace fill in an image based on this polyon. :Parameters: * **image** (*ndarray*) -- image to draw on * **value** (*int | Tuple[int], default=1*) -- value fill in with :returns: the image that has been modified in place :rtype: ndarray .. py:method:: _to_cv_countours(self) OpenCV polygon representation, which is a list of points. Holes are implicitly represented. When another polygon is drawn over an existing polyon via cv2.fillPoly :returns: where each ndarray is of shape [N, 1, 2], where N is the number of points on the boundary, the middle dimension is always 1, and the trailing dimension represents x and y coordinates respectively. :rtype: List[ndarray] .. py:method:: coerce(Polygon, data) :classmethod: Try to autodetermine format of input polygon and coerce it into a kwimage.Polygon. :Parameters: **data** (*object*) -- some type of data that can be interpreted as a polygon. :returns: kwimage.Polygon .. rubric:: Example >>> import kwimage >>> self = kwimage.Polygon.random() >>> self.coerce(self) >>> self.coerce(self.exterior) >>> self.coerce(self.exterior.data) >>> self.coerce(self.data) >>> self.coerce(self.to_geojson()) .. py:method:: from_shapely(Polygon, geom) :classmethod: Convert a shapely polygon to a kwimage.Polygon :Parameters: **geom** (*shapely.geometry.polygon.Polygon*) -- a shapely polygon :returns: kwimage.Polygon .. py:method:: from_wkt(Polygon, data) :classmethod: Convert a WKT string to a kwimage.Polygon :Parameters: **data** (*str*) -- a WKT polygon string :returns: kwimage.Polygon .. rubric:: Example >>> import kwimage >>> data = 'POLYGON ((0.11 0.61, 0.07 0.588, 0.015 0.50, 0.11 0.61))' >>> self = kwimage.Polygon.from_wkt(data) >>> assert len(self.exterior) == 4 .. py:method:: from_geojson(Polygon, data_geojson) :classmethod: Convert a geojson polygon to a kwimage.Polygon :Parameters: **data_geojson** (*dict*) -- geojson data .. rubric:: References https://geojson.org/geojson-spec.html .. rubric:: Example >>> from kwimage.structs.polygon import * # NOQA >>> self = Polygon.random(n_holes=2) >>> data_geojson = self.to_geojson() >>> new = Polygon.from_geojson(data_geojson) .. py:method:: to_shapely(self) .. rubric:: Example >>> # xdoc: +REQUIRES(module:kwplot) >>> # xdoc: +REQUIRES(module:shapely) >>> from kwimage.structs.polygon import * # NOQA >>> self = Polygon.random(n_holes=1) >>> self = self.scale(100) >>> geom = self.to_shapely() >>> print('geom = {!r}'.format(geom)) .. py:method:: to_geojson(self) Converts polygon to a geojson structure :returns: Dict[str, object] .. rubric:: Example >>> import kwimage >>> self = kwimage.Polygon.random() >>> print(self.to_geojson()) .. py:method:: to_wkt(self) Convert a kwimage.Polygon to WKT string .. rubric:: Example >>> import kwimage >>> self = kwimage.Polygon.random() >>> print(self.to_wkt()) .. py:method:: from_coco(cls, data, dims=None) :classmethod: Accepts either new-style or old-style coco polygons .. py:method:: _to_coco(self, style='orig') .. py:method:: to_coco(self, style='orig') :returns: coco-style polygons :rtype: List | Dict .. py:method:: to_multi_polygon(self) .. py:method:: to_boxes(self) Deprecated: lossy conversion use 'bounding_box' instead .. py:method:: centroid(self) :property: .. py:method:: bounding_box(self) Returns an axis-aligned bounding box for the segmentation :returns: kwimage.Boxes .. py:method:: bounding_box_polygon(self) Returns an axis-aligned bounding polygon for the segmentation. .. rubric:: Notes This Polygon will be a Box, not a convex hull! Use shapely for convex hulls. :returns: kwimage.Polygon .. py:method:: copy(self) .. py:method:: clip(self, x_min, y_min, x_max, y_max, inplace=False) Clip polygon to image boundaries. .. rubric:: Example >>> from kwimage.structs.polygon import * >>> self = Polygon.random().scale(10).translate(-1) >>> self2 = self.clip(1, 1, 3, 3) >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> self2.draw(setlim=True) .. py:method:: draw_on(self, image, color='blue', fill=True, border=False, alpha=1.0, copy=False) Rasterizes a polygon on an image. See `draw` for a vectorized matplotlib version. :Parameters: * **image** (*ndarray*) -- image to raster polygon on. * **color** (*str | tuple*) -- data coercable to a color * **fill** (*bool, default=True*) -- draw the center mass of the polygon * **border** (*bool, default=False*) -- draw the border of the polygon * **alpha** (*float, default=1.0*) -- polygon transparency (setting alpha < 1 makes this function much slower). * **copy** (*bool, default=False*) -- if False only copies if necessary .. rubric:: Example >>> # xdoc: +REQUIRES(module:kwplot) >>> from kwimage.structs.polygon import * # NOQA >>> self = Polygon.random(n_holes=1).scale(128) >>> image = np.zeros((128, 128), dtype=np.float32) >>> image = self.draw_on(image) >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> kwplot.imshow(image, fnum=1) .. rubric:: Example >>> import kwimage >>> color = 'blue' >>> self = kwimage.Polygon.random(n_holes=1).scale(128) >>> image = np.zeros((128, 128), dtype=np.float32) >>> # Test drawong on all channel + dtype combinations >>> im3 = np.random.rand(128, 128, 3) >>> im_chans = { >>> 'im3': im3, >>> 'im1': kwimage.convert_colorspace(im3, 'rgb', 'gray'), >>> 'im4': kwimage.convert_colorspace(im3, 'rgb', 'rgba'), >>> } >>> inputs = {} >>> for k, im in im_chans.items(): >>> inputs[k + '_01'] = (kwimage.ensure_float01(im.copy()), {'alpha': None}) >>> inputs[k + '_255'] = (kwimage.ensure_uint255(im.copy()), {'alpha': None}) >>> inputs[k + '_01_a'] = (kwimage.ensure_float01(im.copy()), {'alpha': 0.5}) >>> inputs[k + '_255_a'] = (kwimage.ensure_uint255(im.copy()), {'alpha': 0.5}) >>> outputs = {} >>> for k, v in inputs.items(): >>> im, kw = v >>> outputs[k] = self.draw_on(im, color=color, **kw) >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.figure(fnum=2, doclf=True) >>> kwplot.autompl() >>> pnum_ = kwplot.PlotNums(nCols=2, nRows=len(inputs)) >>> for k in inputs.keys(): >>> kwplot.imshow(inputs[k][0], fnum=2, pnum=pnum_(), title=k) >>> kwplot.imshow(outputs[k], fnum=2, pnum=pnum_(), title=k) >>> kwplot.show_if_requested() .. py:method:: draw(self, color='blue', ax=None, alpha=1.0, radius=1, setlim=False, border=False, linewidth=2) Draws polygon in a matplotlib axes. See `draw_on` for in-memory image modification. :Parameters: * **setlim** (*bool*) -- if True ensures the limits of the axes contains the polygon * **color** (*str | Tuple*) -- coercable color * **alpha** (*float*) -- fill transparency * **setlim** (*bool*) -- if True, modify the x and y limits of the matplotlib axes such that the polygon is can be seen. * **border** (*bool, default=False*) -- if True, draws an edge border on the polygon. * **linewidth** (*bool*) -- width of the border .. todo:: - [ ] Rework arguments in favor of matplotlib standards .. rubric:: Example >>> # xdoc: +REQUIRES(module:kwplot) >>> from kwimage.structs.polygon import * # NOQA >>> self = Polygon.random(n_holes=1) >>> self = self.scale(100) >>> # xdoc: +REQUIRES(--show) >>> self.draw() >>> import kwplot >>> kwplot.autompl() >>> from matplotlib import pyplot as plt >>> kwplot.figure(fnum=2) >>> self.draw(setlim=True) .. py:method:: _ensure_vertex_order(self, inplace=False) Fixes vertex ordering so the exterior ring is CCW and the interior rings are CW. .. rubric:: Example >>> import kwimage >>> self = kwimage.Polygon.random(n=3, n_holes=2, rng=0) >>> print('self = {!r}'.format(self)) >>> new = self._ensure_vertex_order() >>> print('new = {!r}'.format(new)) >>> self = kwimage.Polygon.random(n=3, n_holes=2, rng=0).swap_axes() >>> print('self = {!r}'.format(self)) >>> new = self._ensure_vertex_order() >>> print('new = {!r}'.format(new)) .. py:function:: _is_clockwise(verts) .. rubric:: References https://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order Ignore: verts = poly.data['exterior'].data[::-1] .. py:function:: _order_vertices(verts) .. rubric:: References https://stackoverflow.com/questions/1709283/how-can-i-sort-a-coordinate-list-for-a-rectangle-counterclockwise Ignore: verts = poly.data['exterior'].data[::-1] .. py:class:: MultiPolygon Bases: :py:obj:`kwimage.structs._generic.ObjectList` Data structure for storing multiple polygons (typically related to the same underlying but potentitally disjoing object) :ivar data: :vartype data: List[Polygon] .. py:method:: random(self, n=3, n_holes=0, rng=None, tight=False) :classmethod: Create a random MultiPolygon :returns: MultiPolygon .. py:method:: fill(self, image, value=1) Inplace fill in an image based on this multi-polyon. :Parameters: * **image** (*ndarray*) -- image to draw on (inplace) * **value** (*int | Tuple[int], default=1*) -- value fill in with :returns: the image that has been modified in place :rtype: ndarray .. py:method:: to_multi_polygon(self) .. py:method:: to_boxes(self) Deprecated: lossy conversion use 'bounding_box' instead .. py:method:: bounding_box(self) Return the bounding box of the multi polygon :returns: a Boxes object with one box that encloses all polygons :rtype: kwimage.Boxes .. rubric:: Example >>> from kwimage.structs.polygon import * # NOQA >>> self = MultiPolygon.random(rng=0, n=10) >>> boxes = self.to_boxes() >>> sub_boxes = [d.to_boxes() for d in self.data] >>> areas1 = np.array([s.intersection(boxes).area[0] for s in sub_boxes]) >>> areas2 = np.array([s.area[0] for s in sub_boxes]) >>> assert np.allclose(areas1, areas2) .. py:method:: to_mask(self, dims=None) Returns a mask object indication regions occupied by this multipolygon .. rubric:: Example >>> from kwimage.structs.polygon import * # NOQA >>> s = 100 >>> self = MultiPolygon.random(rng=0).scale(s) >>> dims = (s, s) >>> mask = self.to_mask(dims) >>> # xdoc: +REQUIRES(--show) >>> import kwplot >>> kwplot.autompl() >>> kwplot.figure(fnum=1, doclf=True) >>> from matplotlib import pyplot as pl >>> ax = plt.gca() >>> ax.set_xlim(0, s) >>> ax.set_ylim(0, s) >>> self.draw(color='red', alpha=.4) >>> mask.draw(color='blue', alpha=.4) .. py:method:: to_relative_mask(self) Returns a translated mask such the mask dimensions are minimal. In other words, we move the polygon all the way to the top-left and return a mask just big enough to fit the polygon. :returns: Mask .. py:method:: coerce(cls, data, dims=None) :classmethod: Attempts to construct a MultiPolygon instance from the input data See Mask.coerce .. py:method:: to_shapely(self) .. rubric:: Example >>> # xdoc: +REQUIRES(module:kwplot) >>> # xdoc: +REQUIRES(module:shapely) >>> from kwimage.structs.polygon import * # NOQA >>> self = MultiPolygon.random(rng=0) >>> geom = self.to_shapely() >>> print('geom = {!r}'.format(geom)) .. py:method:: from_shapely(MultiPolygon, geom) :classmethod: Convert a shapely polygon or multipolygon to a kwimage.MultiPolygon .. py:method:: from_geojson(MultiPolygon, data_geojson) :classmethod: Convert a geojson polygon or multipolygon to a kwimage.MultiPolygon .. rubric:: Example >>> import kwimage >>> orig = kwimage.MultiPolygon.random() >>> data_geojson = orig.to_geojson() >>> self = kwimage.MultiPolygon.from_geojson(data_geojson) .. py:method:: to_geojson(self) Converts polygon to a geojson structure .. py:method:: from_coco(cls, data, dims=None) :classmethod: Accepts either new-style or old-style coco multi-polygons .. py:method:: _to_coco(self, style='orig') .. py:method:: to_coco(self, style='orig') .. rubric:: Example >>> from kwimage.structs.polygon import * # NOQA >>> self = MultiPolygon.random(1, rng=0) >>> self.to_coco() .. py:method:: swap_axes(self, inplace=False) .. py:class:: PolygonList Bases: :py:obj:`kwimage.structs._generic.ObjectList` Stores and allows manipluation of multiple polygons, usually within the same image. .. py:method:: to_mask_list(self, dims=None) Converts all items to masks .. py:method:: to_polygon_list(self) .. py:method:: to_segmentation_list(self) Converts all items to segmentation objects .. py:method:: swap_axes(self, inplace=False) .. py:method:: to_geojson(self, as_collection=False) Converts a list of polygons/multipolygons to a geojson structure :Parameters: **as_collection** (*bool*) -- if True, wraps the polygon geojson items in a geojson feature collection, otherwise just return a list of items. :returns: items or geojson data :rtype: List[Dict] | Dict .. rubric:: Example >>> import kwimage >>> data = [kwimage.Polygon.random(), >>> kwimage.Polygon.random(n_holes=1), >>> kwimage.MultiPolygon.random(n_holes=1), >>> kwimage.MultiPolygon.random()] >>> self = kwimage.PolygonList(data) >>> geojson = self.to_geojson(as_collection=True) >>> items = self.to_geojson(as_collection=False) >>> print('geojson = {}'.format(ub.repr2(geojson, nl=-2, precision=1))) >>> print('items = {}'.format(ub.repr2(items, nl=-2, precision=1)))