<commit_before># -*- encoding: utf-8 -*-

"""
    stonemason.provider.tilestorage.cluster
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Metatile split into Tiles.
"""

__author__ = 'kotaimen'
__date__ = '1/18/15'

import collections
import zipfile
import json
import math

import six

from stonemason.util.guesstypes import guess_mimetype, guess_extension
from stonemason.pyramid import Tile, TileIndex, MetaTile, MetaTileIndex
from stonemason.provider.formatbundle import MapWriter
from .exceptions import TileClusterError



# The index file name
CLUSTER_ZIP_INDEX = 'index.json'
CLUSTER_ZIP_INDEX_LEGACY = 'tiles.json'
CLUSTER_ZIP_VERSION = 1


class TileCluster(object):
    """A cluster of `Tiles` split from  a `MetaTile`.

    `TileCluster` can be loaded from a clustered zip file or created from
    `MetaTile`.


    >>> from stonemason.provider.tilestorage import TileCluster
    >>> from stonemason.provider.formatbundle import MapType, TileFormat, FormatBundle
    >>> from stonemason.pyramid import MetaTile, MetaTileIndex, TileIndex
    >>> from PIL import Image
    >>> import io
    >>> image = Image.new('RGB', (1024, 1024))
    >>> buffer = io.BytesIO()
    >>> image.save(buffer, 'JPEG')
    >>> image_data = buffer.getvalue()
    >>> metatile = MetaTile(MetaTileIndex(4, 4, 8, 2),
    ...                     data=image_data,
    ...                     mimetype='image/jpeg',
    ...                     mtime=1.,
    ...                     buffer=256)
    >>> format_bundle = FormatBundle(MapType('image'), TileFormat('JPEG'))
    >>> cluster = TileCluster.from_metatile(metatile, format_bundle.writer)
    >>> cluster.index
    MetaTileIndex(4/4/8@2)
    >>> cluster.tiles
    [Tile(4/4/8), Tile(4/4/9), Tile(4/5/8), Tile(4/5/9)]
    >>> cluster[TileIndex(4, 4, 8)]
    Tile(4/4/8)

    :param index: Metatile index of this cluster.
    :type index: :class:`~stonemason.provider.MetaTileIndex`.
    :param tiles: Tiles of this cluster
    :type index: :class:`~stonemason.provider.Tile`.
    """

    def __init__(self, index, tiles):
        assert isinstance(index, MetaTileIndex)
        assert isinstance(tiles, list)
        self._index = index
        self._tiles = sorted(tiles,
                             key=lambda tile: self._tile_key_func(tile.index))

    def _tile_key_func(self, index):
        return (index.x - self._index.x) * self._index.stride + \
               (index.y - self._index.y)

    @property
    def index(self):
        """:class:`~stonemason.provider.MetaTileIndex` of this cluster."""
        return self._index

    @property
    def tiles(self):
        """A list of :class:`~stonemason.provider.Tile` in this cluster."""
        return self._tiles

    def __getitem__(self, index):
        """Retrieve `Tile` with given index

        :param index: Index of the tile.
        :type index: :class:`~stonemason.provider.MetaTileIndex`
        :return: Tile
        :rtype: :class:`~stonemason.provider.Tile`
        :raise: :class:`~TileClusterError``
        """
        assert isinstance(index, TileIndex)
        if MetaTileIndex.from_tile_index(index,
                                         self._index.stride) != self._index:
            raise TileClusterError('Tile index is not covered in the cluster.')

        row, column = index.x - self.index.x, index.y - self.index.y
        return self._tiles[self._tile_key_func(index)]


    @staticmethod
    def from_metatile(metatile, writer):
        """Create a `TileCluster` object from a `MetaTile`.

        Besides `metatile`, a :class:`~stonemason.provider.formatbundle.MapWriter`
        instance must also be provided.  It is used to re-split metatile
        data into smaller tiles.

        :param metatile: The metatile.
        :type metatile: :class:`~stonemason.provider.tilecache.TileCluster`

        :param writer: A `MapWriter` to resplit metatile data into small tiles.
        :type writer: :class:`~stonemason.provider.formatbundle.MapWriter`

        :return: created cluster object.
        :rtype: :class:`~stonemason.provider.tilestorage.TileCluster`<commit_msg>FIX: Fix docstring errors in tile storage.<commit_after># -*- encoding: utf-8 -*-

"""
    stonemason.provider.tilestorage.cluster
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Metatile split into Tiles.
"""

__author__ = 'kotaimen'
__date__ = '1/18/15'

import collections
import zipfile
import json
import math

import six

from stonemason.util.guesstypes import guess_mimetype, guess_extension
from stonemason.pyramid import Tile, TileIndex, MetaTile, MetaTileIndex
from stonemason.provider.formatbundle import MapWriter
from .exceptions import TileClusterError



# The index file name
CLUSTER_ZIP_INDEX = 'index.json'
CLUSTER_ZIP_INDEX_LEGACY = 'tiles.json'
CLUSTER_ZIP_VERSION = 1


class TileCluster(object):
    """A cluster of `Tiles` split from  a `MetaTile`.

    `TileCluster` can be loaded from a clustered zip file or created from
    `MetaTile`.


    >>> from stonemason.provider.tilestorage import TileCluster
    >>> from stonemason.provider.formatbundle import MapType, TileFormat, FormatBundle
    >>> from stonemason.pyramid import MetaTile, MetaTileIndex, TileIndex
    >>> from PIL import Image
    >>> import io
    >>> image = Image.new('RGB', (1024, 1024))
    >>> buffer = io.BytesIO()
    >>> image.save(buffer, 'JPEG')
    >>> image_data = buffer.getvalue()
    >>> metatile = MetaTile(MetaTileIndex(4, 4, 8, 2),
    ...                     data=image_data,
    ...                     mimetype='image/jpeg',
    ...                     mtime=1.,
    ...                     buffer=256)
    >>> format_bundle = FormatBundle(MapType('image'), TileFormat('JPEG'))
    >>> cluster = TileCluster.from_metatile(metatile, format_bundle.writer)
    >>> cluster.index
    MetaTileIndex(4/4/8@2)
    >>> cluster.tiles
    [Tile(4/4/8), Tile(4/4/9), Tile(4/5/8), Tile(4/5/9)]
    >>> cluster[TileIndex(4, 4, 8)]
    Tile(4/4/8)

    :param index: Metatile index of this cluster.
    :type index: :class:`~stonemason.provider.MetaTileIndex`.
    :param tiles: Tiles of this cluster
    :type index: :class:`~stonemason.provider.Tile`.
    """

    def __init__(self, index, tiles):
        assert isinstance(index, MetaTileIndex)
        assert isinstance(tiles, list)
        self._index = index
        self._tiles = sorted(tiles,
                             key=lambda tile: self._tile_key_func(tile.index))

    def _tile_key_func(self, index):
        return (index.x - self._index.x) * self._index.stride + \
               (index.y - self._index.y)

    @property
    def index(self):
        """:class:`~stonemason.provider.MetaTileIndex` of this cluster."""
        return self._index

    @property
    def tiles(self):
        """A list of :class:`~stonemason.provider.Tile` in this cluster."""
        return self._tiles

    def __getitem__(self, index):
        """Retrieve `Tile` with given index

        :param index: Index of the tile.
        :type index: :class:`~stonemason.provider.MetaTileIndex`
        :return: Tile
        :rtype: :class:`~stonemason.provider.Tile`
        :raise: :class:`~TileClusterError`
        """
        assert isinstance(index, TileIndex)
        if MetaTileIndex.from_tile_index(index,
                                         self._index.stride) != self._index:
            raise TileClusterError('Tile index is not covered in the cluster.')

        row, column = index.x - self.index.x, index.y - self.index.y
        return self._tiles[self._tile_key_func(index)]


    @staticmethod
    def from_metatile(metatile, writer):
        """Create a `TileCluster` object from a `MetaTile`.

        Besides `metatile`, a :class:`~stonemason.provider.formatbundle.MapWriter`
        instance must also be provided.  It is used to re-split metatile
        data into smaller tiles.

        :param metatile: The metatile.
        :type metatile: :class:`~stonemason.provider.tilecache.TileCluster`

        :param writer: A `MapWriter` to resplit metatile data into small tiles.
        :type writer: :class:`~stonemason.provider.formatbundle.MapWriter`

        :return: created cluster object.
        :rtype: :class:`~stonemason.provider.tilestorage.TileCluster`
{'fraction_non_alphanumeric': 0.1282144590004852, 'fraction_numerical': 0.010917030567685589, 'mean_word_length': 2.754553734061931, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 93, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '662985', 'n_tokens_mistral': 2583, 'n_tokens_neox': 2482, 'n_words': 611}