import datetime
from typing import TYPE_CHECKING, Dict, Final, List, Optional, Sequence, Tuple

from telegram import constants
from telegram._chat import Chat
from telegram._messageentity import MessageEntity
from telegram._telegramobject import TelegramObject
from telegram._user import User
from telegram._utils import enum
from telegram._utils.argumentparsing import parse_sequence_arg
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
from telegram._utils.types import JSONDict

if TYPE_CHECKING:
    from telegram import Bot

class PollOption(TelegramObject):
    """
    This object contains information about one answer option in a poll.

    Objects of this class are comparable in terms of equality. Two objects of this class are
    considered equal, if their :attr:`text` and :attr:`voter_count` are equal.

    Args:
        text (:obj:`str`): Option text,
            :tg-const:`telegram.PollOption.MIN_LENGTH`-:tg-const:`telegram.PollOption.MAX_LENGTH`
            characters.
        voter_count (:obj:`int`): Number of users that voted for this option.

    Attributes:
        text (:obj:`str`): Option text,
            :tg-const:`telegram.PollOption.MIN_LENGTH`-:tg-const:`telegram.PollOption.MAX_LENGTH`
            characters.
        voter_count (:obj:`int`): Number of users that voted for this option.

    """

    __slots__ = ("voter_count", "text")

    def __init__(self, text: str, voter_count: int, *, api_kwargs: Optional[JSONDict] = None):
        super().__init__(api_kwargs=api_kwargs)
        self.text: str = text
        self.voter_count: int = voter_count

        self._id_attrs = (self.text, self.voter_count)

        self._freeze()

    MIN_LENGTH: Final[int] = constants.PollLimit.MIN_OPTION_LENGTH
    """:const:`telegram.constants.PollLimit.MIN_OPTION_LENGTH`

    .. versionadded:: 20.0
    """
    MAX_LENGTH: Final[int] = constants.PollLimit.MAX_OPTION_LENGTH
    """:const:`telegram.constants.PollLimit.MAX_OPTION_LENGTH`

    .. versionadded:: 20.0
    """

class PollAnswer(TelegramObject):
    """
    This object represents an answer of a user in a non-anonymous poll.

    Objects of this class are comparable in terms of equality. Two objects of this class are
    considered equal, if their :attr:`poll_id`, :attr:`user` and :attr:`option_ids` are equal.

    .. versionchanged:: 20.5
        The order of :paramref:`option_ids` and :paramref:`user` is changed in
        20.5 as the latter one became optional.

    .. versionchanged:: 20.6
       Backward compatiblity for changed order of :paramref:`option_ids` and :paramref:`user`
       was removed.

    Args:
        poll_id (:obj:`str`): Unique poll identifier.
        option_ids (Sequence[:obj:`int`]): Identifiers of answer options, chosen by the user. May
            be empty if the user retracted their vote.

            .. versionchanged:: 20.0
                |sequenceclassargs|
        user (:class:`telegram.User`, optional): The user that changed the answer to the poll,
            if the voter isn't anonymous. If the voter is anonymous, this field will contain the
            user :tg-const:`telegram.constants.ChatID.FAKE_CHANNEL` for backwards compatibility.

            .. versionchanged:: 20.5
                :paramref:`user` became optional.
        voter_chat (:class:`telegram.Chat`, optional): The chat that changed the answer to the
            poll, if the voter is anonymous.

            .. versionadded:: 20.5

    Attributes:
        poll_id (:obj:`str`): Unique poll identifier.
        option_ids (Tuple[:obj:`int`]): Identifiers of answer options, chosen by the user. May
            be empty if the user retracted their vote.

            .. versionchanged:: 20.0
                |tupleclassattrs|
        user (:class:`telegram.User`): Optional. The user, who changed the answer to the
            poll, if the voter isn't anonymous. If the voter is anonymous, this field will contain
            the user :tg-const:`telegram.constants.ChatID.FAKE_CHANNEL` for backwards compatibility

            .. versionchanged:: 20.5
                :paramref:`user` became optional.
        voter_chat (:class:`telegram.Chat`): Optional. The chat that changed the answer to the
            poll, if the voter is anonymous.

            .. versionadded:: 20.5

    """

    __slots__ = ("option_ids", "poll_id", "user", "voter_chat")

    def __init__(
        self,
        poll_id: str,
        option_ids: Sequence[int],
        user: Optional[User] = None,
        voter_chat: Optional[Chat] = None,
        *,
        api_kwargs: Optional[JSONDict] = None,
    ):
        super().__init__(api_kwargs=api_kwargs)
        self.poll_id: str = poll_id
        self.voter_chat: Optional[Chat] = voter_chat
        self.option_ids: Tuple[int, ...] = parse_sequence_arg(option_ids)
        self.user: Optional[User] = user

        self._id_attrs = (
            self.poll_id,
            self.option_ids,
            self.user,
            self.voter_chat,
        )

        self._freeze()

    @classmethod
    def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["PollAnswer"]:
        """See :meth:`telegram.TelegramObject.de_json`."""
        data = cls._parse_data(data)

        if not data:
            return None

        data["user"] = User.de_json(data.get("user"), bot)
        data["voter_chat"] = Chat.de_json(data.get("voter_chat"), bot)

        return super().de_json(data=data, bot=bot)

class Poll(TelegramObject):
    """
    This object contains information about a poll.

    Objects of this class are comparable in terms of equality. Two objects of this class are
    considered equal, if their :attr:`id` is equal.

    Examples:
        :any:`Poll Bot <examples.pollbot>`

    Args:
        id (:obj:`str`): Unique poll identifier.
        question (:obj:`str`): Poll question, :tg-const:`telegram.Poll.MIN_QUESTION_LENGTH`-
            :tg-const:`telegram.Poll.MAX_QUESTION_LENGTH` characters.
        options (Sequence[:class:`~telegram.PollOption`]): List of poll options.

            .. versionchanged:: 20.0
                |sequenceclassargs|
        is_closed (:obj:`bool`): :obj:`True`, if the poll is closed.
        is_anonymous (:obj:`bool`): :obj:`True`, if the poll is anonymous.
        type (:obj:`str`): Poll type, currently can be :attr:`REGULAR` or :attr:`QUIZ`.
        allows_multiple_answers (:obj:`bool`): :obj:`True`, if the poll allows multiple answers.
        correct_option_id (:obj:`int`, optional): A zero based identifier of the correct answer
            option. Available only for closed polls in the quiz mode, which were sent
            (not forwarded), by the bot or to a private chat with the bot.
        explanation (:obj:`str`, optional): Text that is shown when a user chooses an incorrect
            answer or taps on the lamp icon in a quiz-style poll,
            0-:tg-const:`telegram.Poll.MAX_EXPLANATION_LENGTH` characters.
        explanation_entities (Sequence[:class:`telegram.MessageEntity`], optional): Special
            entities like usernames, URLs, bot commands, etc. that appear in the
            :attr:`explanation`. This list is empty if the message does not contain explanation
            entities.

            .. versionchanged:: 20.0

               * This attribute is now always a (possibly empty) list and never :obj:`None`.
               * |sequenceclassargs|
        open_period (:obj:`int`, optional): Amount of time in seconds the poll will be active
            after creation.
        close_date (:obj:`datetime.datetime`, optional): Point in time (Unix timestamp) when the
            poll will be automatically closed. Converted to :obj:`datetime.datetime`.

            .. versionchanged:: 20.3
                |datetime_localization|

    Attributes:
        id (:obj:`str`): Unique poll identifier.
        question (:obj:`str`): Poll question, :tg-const:`telegram.Poll.MIN_QUESTION_LENGTH`-
            :tg-const:`telegram.Poll.MAX_QUESTION_LENGTH` characters.
        options (Tuple[:class:`~telegram.PollOption`]): List of poll options.

            .. versionchanged:: 20.0
                |tupleclassattrs|
        total_voter_count (:obj:`int`): Total number of users that voted in the poll.
        is_closed (:obj:`bool`): :obj:`True`, if the poll is closed.
        is_anonymous (:obj:`bool`): :obj:`True`, if the poll is anonymous.
        type (:obj:`str`): Poll type, currently can be :attr:`REGULAR` or :attr:`QUIZ`.
        allows_multiple_answers (:obj:`bool`): :obj:`True`, if the poll allows multiple answers.
        correct_option_id (:obj:`int`): Optional. A zero based identifier of the correct answer
            option. Available only for closed polls in the quiz mode, which were sent
            (not forwarded), by the bot or to a private chat with the bot.
        explanation (:obj:`str`): Optional. Text that is shown when a user chooses an incorrect
            answer or taps on the lamp icon in a quiz-style poll,
            0-:tg-const:`telegram.Poll.MAX_EXPLANATION_LENGTH` characters.
        explanation_entities (Tuple[:class:`telegram.MessageEntity`]): Special entities
            like usernames, URLs, bot commands, etc. that appear in the :attr:`explanation`.
            This list is empty if the message does not contain explanation entities.

            .. versionchanged:: 20.0
                |tupleclassattrs|

            .. versionchanged:: 20.0
               This attribute is now always a (possibly empty) list and never :obj:`None`.
        open_period (:obj:`int`): Optional. Amount of time in seconds the poll will be active
            after creation.
        close_date (:obj:`datetime.datetime`): Optional. Point in time when the poll will be
            automatically closed.

            .. versionchanged:: 20.3
                |datetime_localization|

    """

    __slots__ = (
        "total_voter_count",
        "allows_multiple_answers",
        "open_period",
        "options",
        "type",
        "explanation_entities",
        "is_anonymous",
        "close_date",
        "is_closed",
        "id",
        "explanation",
        "question",
        "correct_option_id",
    )

    def __init__(
        self,
        id: str,  # pylint: disable=redefined-builtin
        question: str,
        options: Sequence[PollOption],
        total_voter_count: int,
        is_closed: bool,
        is_anonymous: bool,
        type: str,  # pylint: disable=redefined-builtin
        allows_multiple_answers: bool,
        correct_option_id: Optional[int] = None,
        explanation: Optional[str] = None,
        explanation_entities: Optional[Sequence[MessageEntity]] = None,
        open_period: Optional[int] = None,
        close_date: Optional[datetime.datetime] = None,
        *,
        api_kwargs: Optional[JSONDict] = None,
    ):
        super().__init__(api_kwargs=api_kwargs)
        self.id: str = id
        self.question: str = question
        self.options: Tuple[PollOption, ...] = parse_sequence_arg(options)
        self.total_voter_count: int = total_voter_count
        self.is_closed: bool = is_closed
        self.is_anonymous: bool = is_anonymous
        self.type: str = enum.get_member(constants.PollType, type, type)
        self.allows_multiple_answers: bool = allows_multiple_answers
        self.correct_option_id: Optional[int] = correct_option_id
        self.explanation: Optional[str] = explanation
        self.explanation_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(
            explanation_entities
        )
        self.open_period: Optional[int] = open_period
        self.close_date: Optional[datetime.datetime] = close_date

        self._id_attrs = (self.id,)

        self._freeze()

    @classmethod
    def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Poll"]:
        """See :meth:`telegram.TelegramObject.de_json`."""
        data = cls._parse_data(data)

        if not data:
            return None

        # Get the local timezone from the bot if it has defaults
        loc_tzinfo = extract_tzinfo_from_defaults(bot)

        data["options"] = [PollOption.de_json(option, bot) for option in data["options"]]
        data["explanation_entities"] = MessageEntity.de_list(data.get("explanation_entities"), bot)
        data["close_date"] = from_timestamp(data.get("close_date"), tzinfo=loc_tzinfo)

        return super().de_json(data=data, bot=bot)

    def parse_explanation_entity(self, entity: MessageEntity) -> str:
        """Returns the text from a given :class:`telegram.MessageEntity`.

        Note:
            This method is present because Telegram calculates the offset and length in
            UTF-16 codepoint pairs, which some versions of Python don't handle automatically.
            (That is, you can't just slice ``Message.text`` with the offset and length.)

        Args:
            entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must
                be an entity that belongs to this message.

        Returns:
            :obj:`str`: The text of the given entity.

        Raises:
            RuntimeError: If the poll has no explanation.

        """
        if not self.explanation:
            raise RuntimeError("This Poll has no 'explanation'.")

        entity_text = self.explanation.encode("utf-16-le")
        entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2]

        return entity_text.decode("utf-16-le")

    def parse_explanation_entities(
        self, types: Optional[List[str]] = None
    ) -> Dict[MessageEntity, str]:
        """
        Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`.
        It contains entities from this polls explanation filtered by their ``type`` attribute as
        the key, and the text that each entity belongs to as the value of the :obj:`dict`.

        Note:
            This method should always be used instead of the :attr:`explanation_entities`
            attribute, since it calculates the correct substring from the message text based on
            UTF-16 codepoints. See :attr:`parse_explanation_entity` for more info.

        Args:
            types (List[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the
                    ``type`` attribute of an entity is contained in this list, it will be returned.
                    Defaults to :attr:`telegram.MessageEntity.ALL_TYPES`.

        Returns:
            Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to
            the text that belongs to them, calculated based on UTF-16 codepoints.

        """
        if types is None:
            types = MessageEntity.ALL_TYPES

        return {
            entity: self.parse_explanation_entity(entity)
            for entity in self.explanation_entities
            if entity.type in types
        }

    REGULAR: Final[str] = constants.PollType.REGULAR
    """:const:`telegram.constants.PollType.REGULAR`"""
    QUIZ: Final[str] = constants.PollType.QUIZ
    """:const:`telegram.constants.PollType.QUIZ`"""
    MAX_EXPLANATION_LENGTH: Final[int] = constants.PollLimit.MAX_EXPLANATION_LENGTH
    """:const:`telegram.constants.PollLimit.MAX_EXPLANATION_LENGTH`

    .. versionadded:: 20.0
    """
    MAX_EXPLANATION_LINE_FEEDS: Final[int] = constants.PollLimit.MAX_EXPLANATION_LINE_FEEDS
    """:const:`telegram.constants.PollLimit.MAX_EXPLANATION_LINE_FEEDS`

    .. versionadded:: 20.0
    """
    MIN_OPEN_PERIOD: Final[int] = constants.PollLimit.MIN_OPEN_PERIOD
    """:const:`telegram.constants.PollLimit.MIN_OPEN_PERIOD`

    .. versionadded:: 20.0
    """
    MAX_OPEN_PERIOD: Final[int] = constants.PollLimit.MAX_OPEN_PERIOD
    """:const:`telegram.constants.PollLimit.MAX_OPEN_PERIOD`

    .. versionadded:: 20.0
    """
    MIN_QUESTION_LENGTH: Final[int] = constants.PollLimit.MIN_QUESTION_LENGTH
    """:const:`telegram.constants.PollLimit.MIN_QUESTION_LENGTH`

    .. versionadded:: 20.0
    """
    MAX_QUESTION_LENGTH: Final[int] = constants.PollLimit.MAX_QUESTION_LENGTH
    """:const:`telegram.constants.PollLimit.MAX_QUESTION_LENGTH`

    .. versionadded:: 20.0
    """
    MIN_OPTION_LENGTH: Final[int] = constants.PollLimit.MIN_OPTION_LENGTH
    """:const:`telegram.constants.PollLimit.MIN_OPTION_LENGTH`

    .. versionadded:: 20.0
    """
    MAX_OPTION_LENGTH: Final[int] = constants.PollLimit.MAX_OPTION_LENGTH
    """:const:`telegram.constants.PollLimit.MAX_OPTION_LENGTH`

    .. versionadded:: 20.0
    """
    MIN_OPTION_NUMBER: Final[int] = constants.PollLimit.MIN_OPTION_NUMBER
    """:const:`telegram.constants.PollLimit.MIN_OPTION_NUMBER`

    .. versionadded:: 20.0
    """
    MAX_OPTION_NUMBER: Final[int] = constants.PollLimit.MAX_OPTION_NUMBER
    """:const:`telegram.constants.PollLimit.MAX_OPTION_NUMBER`

    .. versionadded:: 20.0
    """


#!/usr/bin/env python
# encoding: utf-8
import cv2
import numpy as np

"""motion_attention_point_detector.
This is ease motion attention point calculation algorithm.

Example:
        $ python motion_attention_point.py

Todo:
    * cca function

"""
__author__ = "Kevin Lee"
__affiliate__ = "lidengju.com"
__copyright__ = "Copyright 2018, Kevin"
__license__ = "MIT"
__version__ = "0.1"
__maintainer__ = "Kevin Lee"
__email__ = "kevinleeex@foxmail.com"
__status__ = "Development"

class MotionAttention:
    def onChanged(self, val):
        self.diff_thresh = val

    def __init__(self, diff_thresh=25, move_sense=0.05, show_window=True):
        self.contour_area = []
        self.writer = None
        self.font = None
        self.diff_thresh = diff_thresh
        self.move_sense = move_sense
        self.init_flag = True
        self.show_window = show_window
        self.frame = None
        self.frame_w = None  # frame width
        self.frame_h = None  # frame height
        self.frame_area = None  # frame area
        self.gray_frame = None
        self.fusion_frame = None
        self.absdiff_frame = None
        self.previous_frame = None
        self.attention_point = None
        self.cur_contour = None
        self.cur_area = 0

        # show window with track bar.
        if show_window:
            self.window_name = "Motion Detection Result"
            cv2.namedWindow(self.window_name)
            cv2.createTrackbar("Difference Threshold", self.window_name, self.diff_thresh, 100, self.onChanged)

    def setFrame(self):
        """
        Init the frame setting.
        :return:
        """
        self.frame_w = self.frame.shape[1]  # frame width
        self.frame_h = self.frame.shape[0]  # frame height
        self.frame_area = self.frame_w * self.frame_h  # frame area
        self.gray_frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2GRAY)
        self.fusion_frame = np.float32(self.frame)

    def run(self, frame):
        """
        one run
        :param frame: current frame
        :return: current frame with contours, gray frame with motion variation, attention point
        """
        if self.init_flag:
            self.frame = frame
            self.setFrame()
        cur_frame = frame
        diff_frame = cv2.cvtColor(self.gray_frame, cv2.COLOR_GRAY2BGR)
        self.processFrame(cur_frame)

        if self.hasMoved():
            print("Object moved")
            self.attention_point = self.calAttentionPoint()
            print(self.attention_point)
            # draw attention point
            cv2.circle(cur_frame, (self.attention_point[0], self.attention_point[1]), 5, (0, 255, 0), -1)
            cv2.circle(diff_frame, (self.attention_point[0], self.attention_point[1]), 5, (0, 255, 0), -1)

        if self.show_window:
            cv2.drawContours(cur_frame, self.cur_contour, -1, (0, 0, 255), 2)
            cv2.imshow(self.window_name, cur_frame)
            cv2.imshow("Diff", diff_frame)

        return cur_frame, self.gray_frame, self.attention_point

    def processFrame(self, cur_frame):
        """
        Image processing, blur--> compute diff--> gray_frame--> dilation--> eroding
        :param cur_frame: current frame
        :return: result
        """
        cv2.blur(cur_frame, (3, 3), cur_frame)
        # first time to compute
        if self.init_flag:
            self.init_flag = False
            self.absdiff_frame = cur_frame.copy()
            self.previous_frame = cur_frame.copy()
        else:
            cv2.accumulateWeighted(cur_frame, self.fusion_frame, 0.05)  # compute average
        self.previous_frame = cv2.convertScaleAbs(self.fusion_frame)
        self.absdiff_frame = cv2.absdiff(cur_frame, self.previous_frame)  # abs(average - cur_frame)

        self.gray_frame = cv2.cvtColor(self.absdiff_frame, cv2.COLOR_BGR2GRAY)
        cv2.threshold(self.gray_frame, self.diff_thresh, 255, cv2.THRESH_BINARY, self.gray_frame)

        # define kernels
        dilate_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15, 15))
        erode_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
        cv2.dilate(self.gray_frame, dilate_kernel, self.gray_frame)  # dilation
        result = cv2.erode(self.gray_frame, erode_kernel, self.gray_frame)  # eroding
        return result

    def hasMoved(self):
        """
        Judge the object is moving or not.
        :return: is_moved(boolean)
        """
        out, contours, hierarchy = cv2.findContours(self.gray_frame, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        self.cur_contour = contours
        max_contour = []
        contour_areas = []  # area of contours
        self.cur_area = 0
        for i in range(len(contours)):
            c = cv2.contourArea(contours[i])
            if c < 0.005 * self.frame_area:
                # fill small white blob in black
                cv2.drawContours(self.gray_frame, contours, i, (0, 0, 0), -1)
            else:
                contour_areas.append(c)
                max_contour.append(contours[i])
                self.cur_area += c
        self.cur_contour = max_contour
        self.contour_area = contour_areas
        val_area = self.cur_area / self.frame_area  # calculate the ratio of cur_area/frame_area

        if val_area > self.move_sense:
            return True
        else:
            return False

    def calAttentionPoint(self):
        """
        Calculate the weighted mean attention point.
        :return: attention point
        """
        # get center coord of contours
        centroids = []
        area_ratios = []
        for ix, contour in enumerate(self.cur_contour):
            mm = cv2.moments(contour)
            cx = int(mm['m10'] / mm['m00'])
            cy = int(mm['m01'] / mm['m00'])
            center = [cx, cy]
            a_ratio = 0
            if self.cur_area != 0:
                # print(self.cur_area)
                a_ratio = self.contour_area[ix] / self.cur_area
            centroids.append(center)
            area_ratios.append(a_ratio)

        attention_point = np.dot(np.array(area_ratios).transpose(), np.array(centroids)).squeeze()
        return np.int32(attention_point)

    def cca(self, src_img):
        """
        connected component analysis
        :param src_img: source image to analysis
        :return:
        """
        contours = self.cur_contour
        for i in range(len(contours)):
            pass

if __name__ == '__main__':
    detect = MotionAttention(diff_thresh=40, move_sense=0.01)
    capture = cv2.VideoCapture(0)
    while True:
        frame = capture.read()[1]
        detect.run(frame)
        c = cv2.waitKey(1) & 0x100
        if c == 27:
            break


import glob
from glob import glob
import PIL

dir_new = '/Users/rsimons/Desktop/clear/figures/linepng'
dir_old = '/Users/rsimons/Desktop/linepng'

out_dir = '/Users/rsimons/Desktop/clear/figures/linepng/out'
#imgs_comb = np.vstack((np.asarray( i.resize(min_shape) ) for i in imgs ))

fls = glob(dir_new + '/GN2_*_*.sed.png')

ids = unique(array([fl.strip('.sed.png')[-5:] for fl in fls]))

typ = 'sed'
for i, di in enumerate(ids):
    imgs1 = []
    for ii in arange(-1, 4):
        try:
            im1 = PIL.Image.open(dir_new + '/GN2_%i_%s.%s.png'%(ii,di, typ))
        except:
            im1 = PIL.Image.fromarray(zeros((300,1100)), mode = 'RGBA')
        imgs1.append(im1)

    if len(imgs1) > 0:
        min_shape = sorted([(np.sum(i.size), i.size ) for i in imgs1])[0][1]
        imgs_comb1 = np.vstack((np.asarray( i.resize(min_shape) ) for i in imgs1 ))
        imgs_comb_new = PIL.Image.fromarray( imgs_comb1)

        imgs2 = []
        for ii in arange(-1, 4):
            try: im2 = PIL.Image.open(dir_old + '/GN2_%i_%s.%s.png'%(ii,di, typ))
            except: im2 = PIL.Image.fromarray(zeros((300,1100)), mode = 'RGBA')
            imgs2.append(im2)

        if len(imgs2) > 0:
            min_shape = sorted([(np.sum(i.size), i.size ) for i in imgs2])[0][1]
            imgs_comb2 = np.vstack((np.asarray( i.resize(min_shape) ) for i in imgs2 ))
            imgs_comb_old = PIL.Image.fromarray( imgs_comb2)

            imgs = [imgs_comb_old, imgs_comb_new]
            min_shape = sorted([(np.sum(i.size), i.size ) for i in imgs])[0][1]
            imgs_comb = np.hstack((np.asarray( i.resize(min_shape) ) for i in imgs ))
            imgs_comb_all = PIL.Image.fromarray( imgs_comb)

            imgs_comb_all.save(out_dir + '/GN2_%s.%ss.png'%(di, typ))


import gc
import pathlib
import os
import sys
from weakref import proxy
from pathlib import Path

import imageio
import numpy as np
import pytest
import vtk

import pyvista
from pyvista import examples
from pyvista.plotting import system_supports_plotting
from pyvista.plotting.plotting import SUPPORTED_FORMATS

NO_PLOTTING = not system_supports_plotting()

ffmpeg_failed = False
try:
    try:
        import imageio_ffmpeg
        imageio_ffmpeg.get_ffmpeg_exe()
    except ImportError:
        imageio.plugins.ffmpeg.download()
except:
    ffmpeg_failed = True

if __name__ != '__main__':
    OFF_SCREEN = 'pytest' in sys.modules
else:
    OFF_SCREEN = False

pyvista.OFF_SCREEN = OFF_SCREEN
VTK9 = vtk.vtkVersion().GetVTKMajorVersion() >= 9

sphere = pyvista.Sphere()
sphere_b = pyvista.Sphere(1.0)
sphere_c = pyvista.Sphere(2.0)

def _is_vtk(obj):
    try:
        return obj.__class__.__name__.startswith('vtk')
    except Exception:  # old Python sometimes no __class__.__name__
        return False

@pytest.fixture(autouse=True)
def check_gc():
    """Ensure that all VTK objects are garbage-collected by Python."""
    before = set(id(o) for o in gc.get_objects() if _is_vtk(o))
    yield
    pyvista.close_all()
    gc.collect()
    after = [o for o in gc.get_objects() if _is_vtk(o) and id(o) not in before]
    assert len(after) == 0, \
        'Not all objects GCed:\n' + \
        '\n'.join(sorted(o.__class__.__name__ for o in after))

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot(tmpdir):
    tmp_dir = tmpdir.mkdir("tmpdir2")
    filename = str(tmp_dir.join('tmp.png'))
    scalars = np.arange(sphere.n_points)
    cpos, img = pyvista.plot(sphere,
                             off_screen=OFF_SCREEN,
                             full_screen=True,
                             text='this is a sphere',
                             show_bounds=True,
                             color='r',
                             style='wireframe',
                             line_width=10,
                             scalars=scalars,
                             flip_scalars=True,
                             cmap='bwr',
                             interpolate_before_map=True,
                             screenshot=filename,
                             return_img=True)
    assert isinstance(cpos, pyvista.CameraPosition)
    assert isinstance(img, np.ndarray)
    assert os.path.isfile(filename)

    filename = pathlib.Path(str(tmp_dir.join('tmp2.png')))
    cpos = pyvista.plot(sphere, off_screen=OFF_SCREEN, screenshot=filename)

    # Ensure it added a PNG extension by default
    assert filename.with_suffix(".png").is_file()

    # test invalid extension
    with pytest.raises(ValueError):
        filename = pathlib.Path(str(tmp_dir.join('tmp3.foo')))
        pyvista.plot(sphere, off_screen=OFF_SCREEN, screenshot=filename)

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_invalid_style():
    with pytest.raises(ValueError):
        pyvista.plot(sphere, style='not a style')

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_interactor_style():
    plotter = pyvista.Plotter()
    plotter.add_mesh(sphere)
    interactions = (
        'trackball',
        'trackball_actor',
        'image',
        'joystick',
        'zoom',
        'terrain',
        'rubber_band',
        'rubber_band_2d',
    )
    for interaction in interactions:
        getattr(plotter, f'enable_{interaction}_style')()
        assert plotter._style_class is not None
    plotter.close()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_lighting():
    plotter = pyvista.Plotter()

    # test default disable_3_lights()
    lights = list(plotter.renderer.GetLights())
    assert all([light.GetSwitch() for light in lights])

    plotter.enable_3_lights()
    lights = list(plotter.renderer.GetLights())
    headlight = lights.pop(0)
    assert not headlight.GetSwitch()
    for i in range(len(lights)):
        if i < 3:
            assert lights[i].GetSwitch()
        else:
            assert not lights[i].GetSwitch()
    assert lights[0].GetIntensity() == 1.0
    assert lights[1].GetIntensity() == 0.6
    assert lights[2].GetIntensity() == 0.5
    plotter.close()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plotter_shape_invalid():
    # wrong size
    with pytest.raises(ValueError):
        pyvista.Plotter(shape=(1,))
    # not positive
    with pytest.raises(ValueError):
        pyvista.Plotter(shape=(1, 0))
    with pytest.raises(ValueError):
        pyvista.Plotter(shape=(0, 2))
    # not a sequence
    with pytest.raises(TypeError):
        pyvista.Plotter(shape={1, 2})

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_bounds_axes_with_no_data():
    plotter = pyvista.Plotter()
    plotter.show_bounds()
    plotter.close()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_show_grid():
    plotter = pyvista.Plotter()
    plotter.show_grid()
    plotter.add_mesh(sphere)
    plotter.close()

cpos_param = [[(2.0, 5.0, 13.0),
              (0.0, 0.0, 0.0),
              (-0.7, -0.5, 0.3)],
             [-1, 2, -5],  # trigger view vector
             [1.0, 2.0, 3.0],
]
cpos_param.extend(pyvista.plotting.Renderer.CAMERA_STR_ATTR_MAP)
@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
@pytest.mark.parametrize('cpos', cpos_param)
def test_set_camera_position(cpos, sphere):
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(sphere)
    plotter.camera_position = cpos
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
@pytest.mark.parametrize('cpos', [[(2.0, 5.0),
                                   (0.0, 0.0, 0.0),
                                   (-0.7, -0.5, 0.3)],
                                  [-1, 2],
                                  [(1,2,3)],
                                  'notvalid'])
def test_set_camera_position_invalid(cpos, sphere):
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(sphere)
    with pytest.raises(pyvista.core.errors.InvalidCameraError):
        plotter.camera_position = cpos

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_no_active_scalars():
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(sphere)
    with pytest.raises(ValueError):
        plotter.update_scalars(np.arange(5))
    with pytest.raises(ValueError):
        plotter.update_scalars(np.arange(sphere.n_faces))

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_show_bounds():
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(sphere)
    plotter.show_bounds(show_xaxis=False,
                        show_yaxis=False,
                        show_zaxis=False,
                        show_xlabels=False,
                        show_ylabels=False,
                        show_zlabels=False,
                        use_2d=True)
    # And test backwards compatibility
    plotter.add_bounds_axes(show_xaxis=False,
                            show_yaxis=False,
                            show_zaxis=False,
                            show_xlabels=False,
                            show_ylabels=False,
                            show_zlabels=False,
                            use_2d=True)
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_label_fmt():
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(sphere)
    plotter.show_bounds(xlabel='My X', fmt=r'%.3f')
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
@pytest.mark.parametrize('grid', [True, 'both', 'front', 'back'])
@pytest.mark.parametrize('location', ['all', 'origin', 'outer', 'front', 'back'])
def test_plot_show_bounds_params(grid, location):
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(pyvista.Cube())
    plotter.show_bounds(grid=grid, ticks='inside', location=location)
    plotter.show_bounds(grid=grid, ticks='outside', location=location)
    plotter.show_bounds(grid=grid, ticks='both', location=location)
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plotter_scale():
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(sphere)
    plotter.set_scale(10, 10, 10)
    assert plotter.scale == [10, 10, 10]
    plotter.set_scale(5.0)
    plotter.set_scale(yscale=6.0)
    plotter.set_scale(zscale=9.0)
    assert plotter.scale == [5.0, 6.0, 9.0]
    plotter.scale = [1.0, 4.0, 2.0]
    assert plotter.scale == [1.0, 4.0, 2.0]
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_add_scalar_bar():
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(sphere)
    plotter.add_scalar_bar(label_font_size=10, title_font_size=20, title='woa',
                           interactive=True, vertical=True)
    plotter.add_scalar_bar(background_color='white', n_colors=256)

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_invalid_add_scalar_bar():
    with pytest.raises(AttributeError):
        plotter = pyvista.Plotter()
        plotter.add_scalar_bar()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_list():
    pyvista.plot([sphere, sphere_b],
                 off_screen=OFF_SCREEN,
                 style='points')

    pyvista.plot([sphere, sphere_b, sphere_c],
                 off_screen=OFF_SCREEN,
                 style='wireframe')

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_add_lines_invalid():
    plotter = pyvista.Plotter()
    with pytest.raises(TypeError):
        plotter.add_lines(range(10))

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_open_gif_invalid():
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    with pytest.raises(ValueError):
        plotter.open_gif('file.abs')

@pytest.mark.skipif(ffmpeg_failed, reason="Requires imageio-ffmpeg")
@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_make_movie():
    # Make temporary file
    filename = os.path.join(pyvista.USER_DATA_PATH, 'tmp.mp4')

    movie_sphere = sphere.copy()
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.open_movie(filename)
    actor = plotter.add_axes_at_origin()
    plotter.remove_actor(actor, reset_camera=False, render=True)
    plotter.add_mesh(movie_sphere,
                     scalars=np.random.random(movie_sphere.n_faces))
    plotter.show(auto_close=False, window_size=[304, 304])
    plotter.set_focus([0, 0, 0])
    for i in range(3):  # limiting number of frames to write for speed
        plotter.write_frame()
        random_points = np.random.random(movie_sphere.points.shape)
        movie_sphere.points[:] = random_points*0.01 + movie_sphere.points*0.99
        movie_sphere.points[:] -= movie_sphere.points.mean(0)
        scalars = np.random.random(movie_sphere.n_faces)
        plotter.update_scalars(scalars)

    # checking if plotter closes
    ref = proxy(plotter)
    plotter.close()

    # remove file
    os.remove(filename)

    try:
        ref
    except:
        raise RuntimeError('Plotter did not close')

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_add_legend():
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(sphere)
    with pytest.raises(ValueError):
        plotter.add_legend()
    legend_labels = [['sphere', 'r']]
    plotter.add_legend(labels=legend_labels, border=True, bcolor=None,
                       size=[0.1, 0.1])
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_add_axes_twice():
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_axes()
    plotter.add_axes(interactive=True)

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_add_point_labels():
    n = 10
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    points = np.random.random((n, 3))

    with pytest.raises(ValueError):
        plotter.add_point_labels(points, range(n - 1))

    plotter.add_point_labels(points, range(n), show_points=True, point_color='r')
    plotter.add_point_labels(points - 1, range(n), show_points=False, point_color='r')
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
@pytest.mark.parametrize('always_visible', [False, True])
def test_add_point_labels_always_visible(always_visible):
    # just make sure it runs without exception
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_point_labels(
        np.array([[0, 0, 0]]), ['hello world'], always_visible=always_visible)
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_set_background():
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.set_background('k')
    plotter.background_color = "yellow"
    plotter.set_background([0, 0, 0], top=[1,1,1]) # Gradient
    _ = plotter.background_color
    plotter.show()

    plotter = pyvista.Plotter(off_screen=OFF_SCREEN, shape=(1,2))
    plotter.set_background('orange')
    for renderer in plotter.renderers:
        assert renderer.GetBackground() == pyvista.parse_color('orange')
    plotter.show()

    plotter = pyvista.Plotter(off_screen=OFF_SCREEN, shape=(1,2))
    plotter.subplot(0,1)
    plotter.set_background('orange', all_renderers=False)
    assert plotter.renderers[0].GetBackground() != pyvista.parse_color('orange')
    assert plotter.renderers[1].GetBackground() == pyvista.parse_color('orange')
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_add_points():
    n = 10
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    points = np.random.random((n, 3))
    plotter.add_points(points, scalars=np.arange(10), cmap=None, flip_scalars=True)
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_key_press_event():
    plotter = pyvista.Plotter(off_screen=False)
    plotter.key_press_event(None, None)
    plotter.close()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_enable_picking_gc():
    plotter = pyvista.Plotter(off_screen=False)
    sphere = pyvista.Sphere()
    plotter.add_mesh(sphere)
    plotter.enable_cell_picking()
    plotter.close()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_left_button_down():
    plotter = pyvista.Plotter(off_screen=False)
    if VTK9:
        with pytest.raises(ValueError):
            plotter.left_button_down(None, None)
    else:
        plotter.left_button_down(None, None)
    plotter.close()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_show_axes():
    # if not closed correctly, a seg fault occurs when exitting
    plotter = pyvista.Plotter(off_screen=False)
    plotter.show_axes()
    plotter.close()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_update():
    plotter = pyvista.Plotter(off_screen=True)
    plotter.update()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_cell_arrays():
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    scalars = np.arange(sphere.n_faces)
    plotter.add_mesh(sphere, interpolate_before_map=True, scalars=scalars,
                     n_colors=5, rng=10)
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_clim():
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    scalars = np.arange(sphere.n_faces)
    plotter.add_mesh(sphere, interpolate_before_map=True, scalars=scalars,
                     n_colors=5, clim=10)
    plotter.show()
    assert plotter.mapper.GetScalarRange() == (-10, 10)

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_invalid_n_arrays():
    with pytest.raises(ValueError):
        plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
        plotter.add_mesh(sphere, scalars=np.arange(10))
        plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_arrow():
    cent = np.random.random(3)
    direction = np.random.random(3)
    pyvista.plot_arrows(cent, direction, off_screen=OFF_SCREEN)

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_arrows():
    cent = np.random.random((100, 3))
    direction = np.random.random((100, 3))
    pyvista.plot_arrows(cent, direction, off_screen=OFF_SCREEN)

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_axes():
    plotter = pyvista.Plotter(off_screen=True)
    plotter.add_axes()
    plotter.add_mesh(pyvista.Sphere())
    plotter.show()
    plotter = pyvista.Plotter(off_screen=True)
    plotter.add_orientation_widget(pyvista.Cube())
    plotter.add_mesh(pyvista.Cube())
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_box_axes():
    plotter = pyvista.Plotter(off_screen=True)
    plotter.add_axes(box=True, box_args={'color_box':True})
    plotter.add_mesh(pyvista.Sphere())
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_screenshot(tmpdir):
    plotter = pyvista.Plotter(off_screen=True)
    plotter.add_mesh(pyvista.Sphere())
    img = plotter.screenshot(transparent_background=True)
    assert np.any(img)
    img_again = plotter.screenshot()
    assert np.any(img_again)
    filename = str(tmpdir.mkdir("tmpdir").join('export-graphic.svg'))
    plotter.save_graphic(filename)

    # checking if plotter closes
    ref = proxy(plotter)
    plotter.close()

    try:
        ref
    except:
        raise RuntimeError('Plotter did not close')

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
@pytest.mark.parametrize('ext', SUPPORTED_FORMATS)
def test_save_screenshot(tmpdir, sphere, ext):
    filename = str(tmpdir.mkdir("tmpdir").join('tmp' + ext))
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(sphere)
    plotter.screenshot(filename)
    assert os.path.isfile(filename)
    assert Path(filename).stat().st_size

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_scalars_by_name():
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    data = examples.load_uniform()
    plotter.add_mesh(data, scalars='Spatial Cell Data')
    plotter.show()

def test_themes():
    pyvista.set_plot_theme('paraview')
    pyvista.set_plot_theme('document')
    pyvista.set_plot_theme('night')
    pyvista.set_plot_theme('default')

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_multi_block_plot():
    multi = pyvista.MultiBlock()
    multi.append(examples.load_rectilinear())
    uni = examples.load_uniform()
    arr = np.random.rand(uni.n_cells)
    uni.cell_arrays.append(arr, 'Random Data')
    multi.append(uni)
    # And now add a data set without the desired array and a NULL component
    multi[3] = examples.load_airplane()
    with pytest.raises(ValueError):
        # The scalars are not available in all datasets so raises ValueError
        multi.plot(scalars='Random Data', off_screen=OFF_SCREEN, multi_colors=True)
    multi.plot(off_screen=OFF_SCREEN, multi_colors=True)

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_clear():
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(sphere)
    plotter.clear()
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_texture():
    """"Test adding a texture to a plot"""
    globe = examples.load_globe()
    texture = examples.load_globe_texture()
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(globe, texture=texture)
    plotter.show()
    texture.plot(off_screen=OFF_SCREEN)

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_texture_associated():
    """"Test adding a texture to a plot"""
    globe = examples.load_globe()
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(globe, texture=True)
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_read_texture_from_numpy():
    """"Test adding a texture to a plot"""
    globe = examples.load_globe()
    texture = pyvista.numpy_to_texture(imageio.imread(examples.mapfile))
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(globe, texture=texture)
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_rgb():
    """"Test adding a texture to a plot"""
    cube = pyvista.Cube()
    cube.clear_arrays()
    x_face_color = (255, 0, 0)
    y_face_color = (0, 255, 0)
    z_face_color = (0, 0, 255)
    face_colors = np.array([x_face_color,
                            x_face_color,
                            y_face_color,
                            y_face_color,
                            z_face_color,
                            z_face_color,
                            ], dtype=np.uint8)
    cube.cell_arrays['face_colors'] = face_colors
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(cube, scalars='face_colors', rgb=True)
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_multi_component_array():
    """"Test adding a texture to a plot"""
    image = pyvista.UniformGrid((3,3,3))
    image['array'] = np.random.randn(*image.dimensions).ravel(order='f')
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(image, scalars='array')
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_camera():
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(sphere)
    plotter.view_isometric()
    plotter.reset_camera()
    plotter.view_xy()
    plotter.view_xz()
    plotter.view_yz()
    plotter.add_mesh(examples.load_uniform(), reset_camera=True, culling=True)
    plotter.view_xy(True)
    plotter.view_xz(True)
    plotter.view_yz(True)
    plotter.show()
    plotter.camera_position = None

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_multi_renderers():
    plotter = pyvista.Plotter(shape=(2, 2), off_screen=OFF_SCREEN)

    plotter.subplot(0, 0)
    plotter.add_text('Render Window 0', font_size=30)
    sphere = pyvista.Sphere()
    plotter.add_mesh(sphere, scalars=sphere.points[:, 2])
    plotter.add_scalar_bar('Z', vertical=True)

    plotter.subplot(0, 1)
    plotter.add_text('Render Window 1', font_size=30)
    plotter.add_mesh(pyvista.Cube(), show_edges=True)

    plotter.subplot(1, 0)
    plotter.add_text('Render Window 2', font_size=30)
    plotter.add_mesh(pyvista.Arrow(), color='y', show_edges=True)

    plotter.subplot(1, 1)
    plotter.add_text('Render Window 3', position=(0., 0.),
                     font_size=30, viewport=True)
    plotter.add_mesh(pyvista.Cone(), color='g', show_edges=True,
                     culling=True)
    plotter.add_bounding_box(render_lines_as_tubes=True, line_width=5)
    plotter.show_bounds(all_edges=True)

    plotter.update_bounds_axes()
    plotter.show()

    # Test subplot indices (2 rows by 1 column)
    plotter = pyvista.Plotter(shape=(2, 1), off_screen=OFF_SCREEN)
    # First row
    plotter.subplot(0,0)
    plotter.add_mesh(pyvista.Sphere())
    # Second row
    plotter.subplot(1,0)
    plotter.add_mesh(pyvista.Cube())
    plotter.show()

    # Test subplot indices (1 row by 2 columns)
    plotter = pyvista.Plotter(shape=(1, 2), off_screen=OFF_SCREEN)
    # First column
    plotter.subplot(0,0)
    plotter.add_mesh(pyvista.Sphere())
    # Second column
    plotter.subplot(0,1)
    plotter.add_mesh(pyvista.Cube())
    plotter.show()

    with pytest.raises(IndexError):
        # Test bad indices
        plotter = pyvista.Plotter(shape=(1, 2), off_screen=OFF_SCREEN)
        plotter.subplot(0,0)
        plotter.add_mesh(pyvista.Sphere())
        plotter.subplot(1,0)
        plotter.add_mesh(pyvista.Cube())
        plotter.show()

    # Test subplot 3 on left, 1 on right
    plotter = pyvista.Plotter(shape='3|1', off_screen=OFF_SCREEN)
    # First column
    plotter.subplot(0)
    plotter.add_mesh(pyvista.Sphere())
    plotter.subplot(1)
    plotter.add_mesh(pyvista.Cube())
    plotter.subplot(2)
    plotter.add_mesh(pyvista.Cylinder())
    plotter.subplot(3)
    plotter.add_mesh(pyvista.Cone())
    plotter.show()

    # Test subplot 3 on bottom, 1 on top
    plotter = pyvista.Plotter(shape='1|3', off_screen=OFF_SCREEN)
    # First column
    plotter.subplot(0)
    plotter.add_mesh(pyvista.Sphere())
    plotter.subplot(1)
    plotter.add_mesh(pyvista.Cube())
    plotter.subplot(2)
    plotter.add_mesh(pyvista.Cylinder())
    plotter.subplot(3)
    plotter.add_mesh(pyvista.Cone())
    plotter.show()

def test_subplot_groups():
    plotter = pyvista.Plotter(shape=(3,3), groups=[(1,[1,2]),(np.s_[:],0)])
    plotter.subplot(0,0)
    plotter.add_mesh(pyvista.Sphere())
    plotter.subplot(0,1)
    plotter.add_mesh(pyvista.Cube())
    plotter.subplot(0,2)
    plotter.add_mesh(pyvista.Arrow())
    plotter.subplot(1,1)
    plotter.add_mesh(pyvista.Cylinder())
    plotter.subplot(2,1)
    plotter.add_mesh(pyvista.Cone())
    plotter.subplot(2,2)
    plotter.add_mesh(pyvista.Box())
    # Test group overlap
    with pytest.raises(AssertionError):
        # Partial overlap
        pyvista.Plotter(shape=(3,3),groups=[([1,2],[0,1]),([0,1],[1,2])])
    with pytest.raises(AssertionError):
        # Full overlap (inner)
        pyvista.Plotter(shape=(4,4),groups=[(np.s_[:],np.s_[:]),([1,2],[1,2])])
    with pytest.raises(AssertionError):
        # Full overlap (outer)
        pyvista.Plotter(shape=(4,4),groups=[(1,[1,2]),([0,3],np.s_[:])])

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_link_views():
    plotter = pyvista.Plotter(shape=(1, 4), off_screen=OFF_SCREEN)
    sphere = pyvista.Sphere()
    plotter.subplot(0, 0)
    plotter.add_mesh(sphere, smooth_shading=False, show_edges=False)
    plotter.subplot(0, 1)
    plotter.add_mesh(sphere, smooth_shading=True, show_edges=False)
    plotter.subplot(0, 2)
    plotter.add_mesh(sphere, smooth_shading=False, show_edges=True)
    plotter.subplot(0, 3)
    plotter.add_mesh(sphere, smooth_shading=True, show_edges=True)
    with pytest.raises(TypeError):
        plotter.link_views(views='foo')
    plotter.link_views([0, 1])
    plotter.link_views()
    with pytest.raises(TypeError):
        plotter.unlink_views(views='foo')
    plotter.unlink_views([0, 1])
    plotter.unlink_views(2)
    plotter.unlink_views()
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_orthographic_slicer():
    data = examples.load_uniform()
    data.set_active_scalars('Spatial Cell Data')

    slices = data.slice_orthogonal()

    # Orthographic Slicer
    p = pyvista.Plotter(shape=(2,2), off_screen=OFF_SCREEN)

    p.subplot(1,1)
    p.add_mesh(slices, clim=data.get_data_range())
    p.add_axes()
    p.enable()

    p.subplot(0,0)
    p.add_mesh(slices['XY'])
    p.view_xy()
    p.disable()

    p.subplot(0,1)
    p.add_mesh(slices['XZ'])
    p.view_xz(negative=True)
    p.disable()

    p.subplot(1,0)
    p.add_mesh(slices['YZ'])
    p.view_yz()
    p.disable()

    p.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_remove_actor():
    data = examples.load_uniform()
    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_mesh(data, name='data')
    plotter.add_mesh(data, name='data')
    plotter.add_mesh(data, name='data')
    plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_image_properties():
    mesh = examples.load_uniform()
    p = pyvista.Plotter(off_screen=OFF_SCREEN)
    p.add_mesh(mesh)
    p.show(auto_close=False) # DO NOT close plotter
    # Get RGB image
    _ = p.image
    # Get the depth image
    _ = p.get_image_depth()
    p.close()
    p = pyvista.Plotter(off_screen=OFF_SCREEN)
    p.add_mesh(mesh)
    p.store_image = True
    assert p.store_image is True
    p.show() # close plotter
    # Get RGB image
    _ = p.image
    # Get the depth image
    _ = p.get_image_depth()
    p.close()

    # gh-920
    rr = np.array(
        [[-0.5, -0.5, 0], [-0.5, 0.5, 1], [0.5, 0.5, 0], [0.5, -0.5, 1]])
    tris = np.array([[3, 0, 2, 1], [3, 2, 0, 3]])
    mesh = pyvista.PolyData(rr, tris)
    p = pyvista.Plotter(off_screen=OFF_SCREEN)
    p.add_mesh(mesh, color=True)
    p.renderer.camera_position = (0., 0., 1.)
    p.renderer.ResetCamera()
    p.enable_parallel_projection()
    assert p.renderer.camera_set
    p.show(interactive=False, auto_close=False)
    img = p.get_image_depth(fill_value=0.)
    rng = np.ptp(img)
    assert 0.3 < rng < 0.4, rng  # 0.3313504 in testing
    p.close()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_volume_rendering():
    # Really just making sure no errors are thrown
    vol = examples.load_uniform()
    vol.plot(off_screen=OFF_SCREEN, volume=True, opacity='linear')

    plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
    plotter.add_volume(vol, opacity='sigmoid', cmap='jet', n_colors=15)
    plotter.show()

    # Now test MultiBlock rendering
    data = pyvista.MultiBlock(dict(a=examples.load_uniform(),
                                   b=examples.load_uniform(),
                                   c=examples.load_uniform(),
                                   d=examples.load_uniform(),))
    data['a'].rename_array('Spatial Point Data', 'a')
    data['b'].rename_array('Spatial Point Data', 'b')
    data['c'].rename_array('Spatial Point Data', 'c')
    data['d'].rename_array('Spatial Point Data', 'd')
    data.plot(off_screen=OFF_SCREEN, volume=True, multi_colors=True, )

    # Check that NumPy arrays work
    arr = vol["Spatial Point Data"].reshape(vol.dimensions)
    pyvista.plot(arr, off_screen=OFF_SCREEN, volume=True, opacity='linear')

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_compar_four():
    # Really just making sure no errors are thrown
    mesh = examples.load_uniform()
    data_a = mesh.contour()
    data_b = mesh.threshold_percent(0.5)
    data_c = mesh.decimate_boundary(0.5)
    data_d = mesh.glyph()
    pyvista.plot_compare_four(data_a, data_b, data_c, data_d,
                              disply_kwargs={'color':'w'},
                              plotter_kwargs={'off_screen':OFF_SCREEN},)
    return

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_depth_peeling():
    mesh = examples.load_airplane()
    p = pyvista.Plotter(off_screen=OFF_SCREEN)
    p.add_mesh(mesh)
    p.enable_depth_peeling()
    p.disable_depth_peeling()
    p.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
@pytest.mark.skipif(os.name == 'nt', reason="No testing on windows for EDL")
def test_plot_eye_dome_lighting():
    mesh = examples.load_airplane()
    mesh.plot(off_screen=OFF_SCREEN, eye_dome_lighting=True)
    p = pyvista.Plotter(off_screen=OFF_SCREEN)
    p.add_mesh(mesh)
    p.enable_eye_dome_lighting()
    p.show()

    p = pyvista.Plotter(off_screen=OFF_SCREEN)
    p.add_mesh(mesh)
    p.enable_eye_dome_lighting()
    p.disable_eye_dome_lighting()
    p.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_opacity_by_array():
    mesh = examples.load_uniform()
    opac = mesh['Spatial Point Data'] / mesh['Spatial Point Data'].max()
    # Test with opacity array
    mesh['opac'] = opac
    p = pyvista.Plotter(off_screen=OFF_SCREEN)
    p.add_mesh(mesh, scalars='Spatial Point Data', opacity='opac',)
    p.show()
    # Test with uncertainty array (transparency)
    mesh['unc'] = opac
    p = pyvista.Plotter(off_screen=OFF_SCREEN)
    p.add_mesh(mesh, scalars='Spatial Point Data', opacity='unc',
               use_transparency=True)
    p.show()
    # Test using mismatched arrays
    with pytest.raises(ValueError):
        p = pyvista.Plotter(off_screen=OFF_SCREEN)
        p.add_mesh(mesh, scalars='Spatial Cell Data', opacity='unc',)
        p.show()
    # Test with user defined transfer function
    opacities = [0,0.2,0.9,0.2,0.1]
    p = pyvista.Plotter(off_screen=OFF_SCREEN)
    p.add_mesh(mesh, scalars='Spatial Point Data', opacity=opacities,)
    p.show()

def test_opacity_transfer_functions():
    n = 256
    mapping = pyvista.opacity_transfer_function('linear', n)
    assert len(mapping) == n
    mapping = pyvista.opacity_transfer_function('sigmoid_10', n)
    assert len(mapping) == n
    with pytest.raises(KeyError):
        mapping = pyvista.opacity_transfer_function('foo', n)
    with pytest.raises(RuntimeError):
        mapping = pyvista.opacity_transfer_function(np.linspace(0, 1, 2*n), n)
    foo = np.linspace(0, n, n)
    mapping = pyvista.opacity_transfer_function(foo, n)
    assert np.allclose(foo, mapping)
    foo = [0,0.2,0.9,0.2,0.1]
    mapping = pyvista.opacity_transfer_function(foo, n, interpolate=False)
    assert len(mapping) == n
    foo = [3, 5, 6, 10]
    mapping = pyvista.opacity_transfer_function(foo, n)
    assert len(mapping) == n

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_closing_and_mem_cleanup():
    n = 5
    for _ in range(n):
        for _ in range(n):
            p = pyvista.Plotter(off_screen=OFF_SCREEN)
            for k in range(n):
                p.add_mesh(pyvista.Sphere(radius=k))
            p.show()
        pyvista.close_all()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_above_below_scalar_range_annotations():
    p = pyvista.Plotter(off_screen=OFF_SCREEN)
    p.add_mesh(examples.load_uniform(), clim=[100, 500], cmap='viridis',
               below_color='blue', above_color='red')
    p.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_user_annotations_scalar_bar():
    p = pyvista.Plotter(off_screen=OFF_SCREEN)
    p.add_mesh(examples.load_uniform(), annotations={100.:'yum'})
    p.show()
    p = pyvista.Plotter(off_screen=OFF_SCREEN)
    p.add_volume(examples.load_uniform(), annotations={100.:'yum'})
    p.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_plot_string_array():
    mesh = examples.load_uniform()
    labels = np.empty(mesh.n_cells, dtype='<U10')
    labels[:] = 'High'
    labels[mesh['Spatial Cell Data'] < 300] = 'Medium'
    labels[mesh['Spatial Cell Data'] < 100] = 'Low'
    mesh['labels'] = labels
    p = pyvista.Plotter(off_screen=OFF_SCREEN)
    p.add_mesh(mesh, scalars='labels')
    p.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_fail_plot_table():
    """Make sure tables cannot be plotted"""
    table = pyvista.Table(np.random.rand(50, 3))
    with pytest.raises(TypeError):
        pyvista.plot(table)
    with pytest.raises(TypeError):
        plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
        plotter.add_mesh(table)

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_bad_keyword_arguments():
    """Make sure bad keyword arguments raise an error"""
    mesh = examples.load_uniform()
    with pytest.raises(TypeError):
        pyvista.plot(mesh, foo=5, off_screen=OFF_SCREEN)
    with pytest.raises(TypeError):
        pyvista.plot(mesh, scalar=mesh.active_scalars_name, off_screen=OFF_SCREEN)
    with pytest.raises(TypeError):
        plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
        plotter.add_mesh(mesh, scalar=mesh.active_scalars_name)
        plotter.show()
    with pytest.raises(TypeError):
        plotter = pyvista.Plotter(off_screen=OFF_SCREEN)
        plotter.add_mesh(mesh, foo="bad")
        plotter.show()

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_cmap_list():
    mesh = sphere.copy()

    n = mesh.n_points
    scalars = np.empty(n)
    scalars[:n//3] = 0
    scalars[n//3:2*n//3] = 1
    scalars[2*n//3:] = 2

    with pytest.raises(TypeError):
        mesh.plot(off_screen=OFF_SCREEN,
                  scalars=scalars, cmap=['red', None, 'blue'])

    mesh.plot(off_screen=OFF_SCREEN,
              scalars=scalars, cmap=['red', 'green', 'blue'])

@pytest.mark.skipif(NO_PLOTTING, reason="Requires system to support plotting")
def test_default_name_tracking():
    N = 10
    color = "tan"

    p = pyvista.Plotter(off_screen=OFF_SCREEN)
    for i in range(N):
        for j in range(N):
            center = (i, j, 0)
            mesh = pyvista.Sphere(center=center)
            p.add_mesh(mesh, color=color)
    n_made_it = len(p.renderer._actors)
    p.show()
    assert n_made_it == N**2

    # release attached scalars
    mesh.ReleaseData()
    del mesh

@pytest.mark.parametrize("as_global", [True, False])
def test_add_background_image(as_global):
    plotter = pyvista.Plotter()
    plotter.add_mesh(sphere)
    plotter.add_background_image(examples.mapfile, as_global=as_global)
    plotter.show()

def test_add_background_image_subplots():
    pl = pyvista.Plotter(shape=(2, 2))
    pl.add_background_image(examples.mapfile, scale=1, as_global=False)
    pl.add_mesh(examples.load_airplane())
    pl.subplot(1, 1)
    pl.add_background_image(examples.mapfile, scale=1, as_global=False)
    pl.add_mesh(examples.load_airplane())
    pl.remove_background_image()

    # should error out as there's no background
    with pytest.raises(RuntimeError):
        pl.remove_background_image()

    pl.add_background_image(examples.mapfile, scale=1, as_global=False)
    pl.show()

def test_add_remove_floor():
    pl = pyvista.Plotter()
    pl.add_mesh(sphere)
    pl.add_floor(color='b', line_width=2, lighting=True)
    pl.add_bounding_box()  # needed for update_bounds_axes
    assert len(pl.renderer._floors) == 1
    pl.add_mesh(sphere_b)
    pl.update_bounds_axes()
    assert len(pl.renderer._floors) == 1
    pl.show()

    pl = pyvista.Plotter()
    pl.add_mesh(sphere)
    pl.add_floor(color='b', line_width=2, lighting=True)
    pl.remove_floors()
    assert not pl.renderer._floors
    pl.show()

def test_reset_camera_clipping_range():
    pl = pyvista.Plotter()
    pl.add_mesh(sphere)

    default_clipping_range = pl.camera.GetClippingRange() # get default clipping range
    assert default_clipping_range != (10,100) # make sure we assign something different than default

    pl.camera.SetClippingRange(10,100) # set clipping range to some random numbers
    assert pl.camera.GetClippingRange() == (10,100) # make sure assignment is successful

    pl.reset_camera_clipping_range()
    assert pl.camera.GetClippingRange() ==  default_clipping_range
    assert pl.camera.GetClippingRange() != (10,100)

def test_index_vs_loc():
    # first: 2d grid
    pl = pyvista.Plotter(shape=(2, 3))
    # index_to_loc valid cases
    vals = [0, 2, 4]
    expecteds = [(0, 0), (0, 2), (1, 1)]
    for val,expected in zip(vals, expecteds):
        assert tuple(pl.index_to_loc(val)) == expected
    # loc_to_index valid cases
    vals = [(0, 0), (0, 2), (1, 1)]
    expecteds = [0, 2, 4]
    for val,expected in zip(vals, expecteds):
        assert pl.loc_to_index(val) == expected
        assert pl.loc_to_index(expected) == expected
    # failing cases
    with pytest.raises(TypeError):
        pl.loc_to_index({1, 2})
    with pytest.raises(TypeError):
        pl.index_to_loc(1.5)
    with pytest.raises(TypeError):
        pl.index_to_loc((1, 2))

    # then: "1d" grid
    pl = pyvista.Plotter(shape='2|3')
    # valid cases
    for val in range(5):
        assert pl.index_to_loc(val) == val
        assert pl.index_to_loc(np.int_(val)) == val
        assert pl.loc_to_index(val) == val
        assert pl.loc_to_index(np.int_(val)) == val


# **************************************************************************
# *
# * Authors:     J.M. De la Rosa Trevin (jmdelarosa@cnb.csic.es)
# *
# * Unidad de  Bioinformatica of Centro Nacional de Biotecnologia , CSIC
# *
# * This program is free software; you can redistribute it and/or modify
# * it under the terms of the GNU General Public License as published by
# * the Free Software Foundation; either version 2 of the License, or
# * (at your option) any later version.
# *
# * This program is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# * GNU General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with this program; if not, write to the Free Software
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# * 02111-1307  USA
# *
# *  All comments concerning this program package may be sent to the
# *  e-mail address 'scipion@cnb.csic.es'
# *
# **************************************************************************
"""
Several Image tools using Matplotlib.
"""

import Tkinter as tk
import matplotlib
import numpy as np

try:
    matplotlib.use('TkAgg')
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
    import matplotlib.pyplot as plt
except:
    #matplotlib.use('Agg')
    plt = None

from matplotlib.figure import Figure
import matplotlib.cm as cm
from matplotlib.patches import Wedge

import xmippLib
import pyworkflow.gui as pwgui

class FigureFrame(tk.Frame):
    """ Create a Tk Frame that will contains a
    Matplotlib Figure.
    **kwargs arguments will be passed to Figure constructor.
    Valid options are:
        figsize = (xdim, ydim)
        dpi = value
        frameon = (True|False)
    """
    def __init__(self, parent, **kwargs):
        tk.Frame.__init__(self, parent)
        self.figure = Figure(**kwargs)
        self.canvas = FigureCanvasTkAgg(self.figure, master=self)
        self.canvas.get_tk_widget().grid(column=0, row=0)

    def getFigure(self):
        return self.figure

    def getCanvas(self):
        return self.canvas

class Preview(tk.Frame):
    #def __init__(self, parent, dim, dpi=36, label=None):
    def __init__(self, parent, dim, dpi=36, label=None, col=0, row=0):
        tk.Frame.__init__(self, parent)
        self.dim = dim
        self.bg = np.zeros((int(dim), int(dim)), float)
        ddim = dim/dpi
        self.figure = Figure(figsize=(ddim, ddim), dpi=dpi, frameon=False)
        self.canvas = FigureCanvasTkAgg(self.figure, master=self)
        self.canvas.get_tk_widget().grid(column=0, row=0)#, sticky=(N, W, E, S))
        if label:
            tk.Label(self, text=label).grid(column=0, row=1)
        self._createAxes()

    def setWindowTitle(self,title):
        """ Set window title"""
        self.canvas.set_window_title(title)

    def _createAxes(self):
        """ Should be implemented in subclasses. """
        pass

    def _update(self):
        """ Should be implemented in subclasses. """
        pass

    def clear(self):
        self._update(self.bg)

    def updateData(self, Z):
        self.clear()
        self._update(Z)

class ImagePreview(Preview):
    def __init__(self, parent, dim, dpi=36, label=None, col=0):
        Preview.__init__(self, parent, dim, dpi, label, col)

    def _createAxes(self):
        ax = self.figure.add_axes([0,0,1,1], frameon=False)
        self.figureimg = ax.imshow(self.bg, cmap=cm.gray)#, extent=[-h, h, -h, h])
        ax.set_axis_off()
        self.ax = ax

    def _update(self, Z, *args):
        self.figureimg.set_data(Z)
        self.figureimg.autoscale()
        self.figureimg.set(extent=[0, Z.shape[1], 0, Z.shape[0]])
        self.canvas.draw()

class PsdPreview(Preview):
    def __init__(self, master, dim, lf, hf, dpi=72, label="PSD", Z=None):
        Preview.__init__(self, master, dim, dpi, label)
        self.lf = lf
        self.hf = hf
        if self.ring:
            self.createRing()
        else:
            self.canvas.draw()

    def _createAxes(self):
        #axdef = SubplotZero(self.figure, 111)
        #ax = self.figure.add_subplot(axdef)
        ax = self.figure.add_axes([0.1,0.1,0.8,0.8], frameon=False)
        #ax.xaxis.set_offset_position(0.5)
        #ax.set_position([0, 0, 1, 1])
        h = 0.5
        ax.set_xlim(-h, h)
        ax.set_ylim(-h, h)
        ax.grid(True)
        self.ring = None
        self.img = ax.imshow(self.bg, cmap=cm.gray, extent=[-h, h, -h, h])
        self.ax = ax

    def createRing(self):
        radius = float(self.hf)
        width = radius - float(self.lf)
        self.ring = Wedge((0,0), radius, 0, 360, width=width, alpha=0.15) # Full ring
        self.ax.add_patch(self.ring)
        self.canvas.draw()

    def updateFreq(self, lf, hf):
        self.lf = lf
        self.hf = hf
        if self.ring:
            self.ring.remove()
            self.ring = None
        if self.hf:
            self.createRing()

    def _update(self, Z):
        if self.ring:
            self.ring.remove()
            self.ring = None
        if self.hf:
            self.createRing()
        self.img.set_data(Z)
        self.img.autoscale()
        self.canvas.draw()

class MaskPreview(ImagePreview):
    def __init__(self, parent, dim, dpi=36, label=None, col=0, outerRadius=None, innerRadius=0):
        ImagePreview.__init__(self, parent, dim, dpi, label, col)
        if outerRadius is None:
            outerRadius = dim / 2
        self.ring = None
        self.updateMask(outerRadius, innerRadius)

    def updateMask(self, outerRadius, innerRadius=0):
        if self.ring is not None:
            self.ring.remove()
        center = self.dim / 2
        width = outerRadius - innerRadius
        self.ring = Wedge((center, center), outerRadius, 0, 360, width=width, alpha=0.15) # Full ring
        self.ax.add_patch(self.ring)
        self.canvas.draw()

class ImageWindow(pwgui.Window):
    #TODO: change filename (now with xmipp format) by location (index, fn as in scipion)
    # this will require to put a wrapper to readPreview in ImageHandler that accepts location
    def __init__(self, filename=None, dim=256, dpi=96, image=None, label=None):
        pwgui.Window.__init__(self, minsize=None)
        if image is None:
            image = xmippLib.Image()
            if dim is None:
                image.read(filename)
                dim, _, _, _ = image.getDimensions()
            else:
                image.readPreview(filename, dim)

        dpi = min(dpi, dim)

        self.imagePreview = ImagePreview(self.root, dim, dpi, label, 0)

        if image is None and filename is None:
            raise Exception("ImageWindow: image or filename should be provided.")

        if filename is None:
            filename = "No filename"

        self.updateImage(image)

        self.imagePreview.grid(row=0, column=0)

    def updateData(self, Z):
        self.imagePreview.updateData(Z)

    def updateImage(self, image):
        self.updateData(image.getData())

def getPngData(filename):
    import matplotlib.image as mpimg
    return mpimg.imread(filename)

def createBgImage(dim):
    return np.ones((dim, dim, 3))


from scipy.spatial import distance
import matplotlib.pyplot as plt
import numpy as np

class Parser:
    def __init__(self, nodesFile):
        self.nodeFile = nodesFile
        self.totalDemand = 0
        self.nodesbynum = {}
        self.nodesbypos = {}
        self.points = []

        self.Nodes = self.get_Nodes()

    def get_Nodes(self):
        counter = 1
        with open("/Users/Jurgen/PycharmProjects/maxcoverageproblem/data/nodesfile" + self.nodeFile) as file:
            for line in file:
                p = line.split(" ")
                self.nodesbypos[(int(p[0]),int(p[1]))] = counter
                self.nodesbynum[counter] = (int(p[0]),int(p[1]))
                counter += 1
                self.points.append((int(p[0]),int(p[1])))

        finalNodes = {}
        for p1 in self.points:
            finalNodes[self.nodesbypos[p1]] = {}
            covered_nodes = {}
            for p2 in self.points:
                if p1 != p2:
                    covered_nodes[self.nodesbypos[p2]] = distance.euclidean(p1,p2)

            finalNodes[self.nodesbypos[p1]]["e"] = covered_nodes

        counter = 1
        with open("/Users/Jurgen/PycharmProjects/maxcoverageproblem/data/demandfiles" + self.nodeFile) as file2:
            total = 0
            for line in file2:
                finalNodes[counter]["d"] = int(line.split()[0])
                total += int(line.split()[0])
                counter += 1

        self.totalDemand = total

        return finalNodes


import requests,random,bs4,time,re,csv,json,os
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
import pandas as pd
from tqdm import tqdm

'''修改程序运行路径'''
# os.chdir(r"D:\vscode\py_bank\171250619\code") #修改自己的运行路径

'''打开文件夹,准备写入'''
word_name='微博信息20191208-20200122'  #爬取文件名字（这个是自己选取内容时进行更改的）
# fp = open(r'../out_put/'+word_name+'.csv','w',encoding = 'gb18030',errors='ignore',newline = '')
fp = open(word_name+'.csv','w',encoding = 'utf-8',errors='ignore',newline = '')
writer = csv.writer(fp)
header = ['名字','正文']
writer.writerow(header)
headers = {
        'User-Agent':UserAgent().random,
        'Cookie': '_T_WM=71684594584; WEIBOCN_FROM=1110006030; SCF=AmUw98emhahMgXvZwoANU7lhckxdUibWuaO3QOtfDtQ7LyKJCnsgF3QKVBEZJCBknNrm9tg0ANXWeV2a9KLbe34.; SUB=_2A25NCllwDeRhGeBK7lsS9i7OzDiIHXVu9Wc4rDV6PUNbktAKLRH6kW1NR7PcUReZucwthAhQZCRrMEssbo25CKew; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9WWh4aV2v7TrAnyZcrkj8Fqd5JpX5KMhUgL.FoqXSK.0So5ES0B2dJLoIEXLxK-L1KeL1hnLxK-L1KeL1hnLxK-LBo5L12qLxK-LBo5L12qLxK.L1KMLB-zt; SSOLoginState=1611540768; ALF=1614132768' ##使用自己的cookie
        }

def save_data(datas):
    writer.writerow(datas)

def get_txt():
    start=1
    end=19   #控制爬取页数
    url = 'https://s.weibo.com/weibo?q=%E7%96%AB%E6%83%85&xsort=hot&suball=1&timescope=custom:2019-12-08:2020-01-22&Refer=SWeibo_box&page={}'
    for page in tqdm(range(start, end+1)):
        url_0 = url.format(page)
    # url='https://s.weibo.com/weibo?q=%E6%96%B0%E5%86%A0%E8%82%BA%E7%82%8E&xsort=hot&suball=1&timescope=custom:2019-12-08:2020-01-22&Refer=g'
        response=requests.get(url_0,headers=headers,timeout=60)
        bs = bs4.BeautifulSoup(response.text,'html.parser')  #翻译
        txts=bs.find(class_='m-con-l').find_all(class_='card-wrap')
        for txt in txts:
            try:
                text=txt.find_all(class_='txt')[1].text.replace(' ','')
            except:
                text=txt.find(class_='txt').text.replace(' ','')
            name=txt.find(class_='name').text.replace(' ','')
            tm=txt.find(class_='from').text.replace(' ','')
            datas=[name,text,tm]
            save_data(datas)
            # print(name,text,tm)

def get_pinglun(mid):
    # url='https://weibo.com/aj/v6/comment/big?ajwvr=6&id=4569792616335323&page=1'
    start=1
    end=300
    url='https://weibo.com/aj/v6/comment/big?ajwvr=6&id='+mid+'&page={}'
    for page in tqdm(range(start, end+1)):
        try:
            url_1 = url.format(page)
            headers = {
            'Cookie':'SINAGLOBAL=6724017172360.135.1589953599360; ALF=1639707749; SCF=AudkK3cK3Lgk2mt7eZjqhseOiGS0AHeJI8R5tVaWQdUMUx0ycGDfGwK86cyvEFE94dXyMIiymb3Ww7yaRW7-2Kw.; SUB=_2A25y3oXKDeRhGedG4lMX8SvNzzyIHXVuICuCrDV8PUJbkNANLW3ekW1NUOlHA2NH5fIasTRofeSEvBWMggjK-yyk; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9WWMWxz-qrW.ZMNG.x4QnFD25NHD95Qp1h.pSo2feKB7Ws4DqcjVi--Ri-8siKL2i--Xi-zRiKy2i--fi-2XiKLWi--fiKnfiKnRi--NiK.XiKLsC5tt; UOR=www.ujiuye.cn,widget.weibo.com,www.baidu.com; wvr=6; _s_tentry=www.baidu.com; Apache=9297895961589.357.1611540283733; ULV=1611540283851:15:2:1:9297895961589.357.1611540283733:1610502750041; webim_unReadCount=%7B%22time%22%3A1611542228829%2C%22dm_pub_total%22%3A0%2C%22chat_group_client%22%3A999%2C%22chat_group_notice%22%3A0%2C%22allcountNum%22%3A1217%2C%22msgbox%22%3A0%7D; WBStorage=8daec78e6a891122|undefined', ##使用自己的cookie
            'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36 Edg/85.0.564.44',
            }

            response=requests.get(url_1,headers=headers,timeout=60)
            # response.encoding='ks_c_5601'
            json_html = json.loads(response.text)
            text=json_html['data']['html']
            # print(text)
            bs = bs4.BeautifulSoup(text,'html.parser')  #翻译
            pingluns=bs.find(class_='list_box').find_all(class_='list_li S_line1 clearfix')
            for pinglun in pingluns:
                pinglun_1=pinglun.find(class_='list_con').find(class_='WB_text').text.split('：')
                name_pinlun=pinglun_1[0]
                try:
                    pinglun_text=pinglun_1[1].split(':')[1]
                except:
                    pinglun_text=pinglun_1[1]

                # dianzan_pinlun=pinglun.find(class_='WB_func clearfix').find(class_='line S_line1').text
                # datas.append([name,zw,name_pinlun,pinglun_text])
                datas=[name_pinlun,pinglun_text]
                save_data(datas)
                # print(name_pinlun,pinglun_text)
        except Exception as e:
            print(str(e))

def main():
    get_txt() #获取微博正文

    # mids=['4469445265017563','4469413552023980']
    # for mid in mids:
    #     get_pinglun(mid) #获取微博评论

if __name__ == "__main__":

    main()
    fp.close()


r"""
Script to display the different percentiles for each column in a list
of tuples. The exact percentile is supplied as a parameter.

For instance, if the supplied percentile is 25 and there are three elements
in each tuple, this script will first show the 25th, 50th, 75th and 100th
percentile of the first column; then it will show the same percentiles for
the second and third columns.

Also note that likelihoods are assumed to be log likelihoods.
As a result, the lowest likelihoods are in the lower percentiles.

This script assumes the supplied percentile is a divisor of 100.
"""

import argparse
import os
import pickle

from os.path import abspath, dirname, join

import scipy
from scipy.stats import spearmanr

HERE = dirname(abspath(__file__))

def show_percentiles(tuples):
    num_tuples = len(tuples)
    num_columns = len(tuples[0])
    for column in range(0, num_columns):
        ordered_tuples = sorted(tuples, key=lambda x: -x[column])
        # note: range is end-exclusive, so 101
        fractions = (p / 100.0 for p in range(percentile, 101, percentile))
        for fraction in fractions:
            index = int(round(fraction * num_tuples) - 1)
            value = ordered_tuples[index][column]
            output = 'Column {column}; Fraction {fraction}; Value {value}'.format(**locals())
            print(output)

def show_correlations(tuples):
    for column_a in range(0, len(tuples[0])):
        array_a = scipy.array([t[column_a] for t in tuples])
        for column_b in range(column_a + 1, len(tuples[0])):
            array_b = scipy.array([t[column_b] for t in tuples])
            spearman = spearmanr(array_a, array_b)
            message = 'Spearman between columns {a} and {b}: {s}'
            print(message.format(a=column_a, b=column_b, s=spearman))

if __name__ == '__main__':

    arg_parser = argparse.ArgumentParser()
    arg_parser.add_argument('percentile')
    arg_parser.add_argument('tuples')
    args = arg_parser.parse_args()
    percentile = int(args.percentile)
    tuples_path = args.tuples
    assert 100 % percentile == 0

    with open(tuples_path, 'rb') as fh:
        tuples = pickle.load(fh)
    show_percentiles(tuples)
    show_correlations(tuples)


from typing import Any

from fastapi import APIRouter, Depends, Form, Query, status
from main.dependencies import get_manager
from main.exception_handlers import HTTPException
from main.serializers import Response
from user.dependencies import get_current_active_user
from user.models import User

from streaming import serializers
from streaming.models import Streaming

router = APIRouter()

@router.get("/")
async def list_streaming(per_page: int = Query(10, gt=0),page: int = Query(1, gt=0)) -> Any:
    data = await Streaming.objects.select_related("user").paginate(page,per_page).all()
    total= await Streaming.objects.count()
    return Response(serializers.Streaming,data=data,per_page=per_page,page=page,total=total)()

@router.get("/{id}")
async def retrieve_streaming(id: int) -> Any:
    instance = await Streaming.objects.get_or_none(id=id)
    if not instance:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,detail="This streaming room is unavailable.")
    return Response(serializers.Streaming,data=instance)()

@router.post("/")
async def create_streaming(
    streaming_in: serializers.StreamingCreate,
    user : User =Depends(get_current_active_user)
    ) -> Any:
    streaming_in = streaming_in.dict()
    streaming_in['status'] = serializers.StreamingStatus.pause
    streaming_in['user'] = user
    instance = await Streaming.objects.create(**streaming_in)

    return Response(serializers.Streaming,data=instance)()

# @router.post("/on_publish")
# async def streaming_publish(
#     name: int = Form(...),
#     manager: StreamingManager = Depends(get_manager(StreamingManager)),
#     ) -> Any:

#     instance = await manager.get(id=name)

#     if not instance:
#         raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,detail="This streaming room is unavailable.")

#     streaming_in = serializers.StreamingUpdate(status=serializers.StreamingStatus.playing)
#     instance = serializers.Streaming(**instance)
#     instance = await manager.update(instance=instance,update_object=streaming_in)

#     return Response(serializers.Streaming,data=instance)()

# @router.post("/on_publish_done")
# async def streaming_done(
#     name: int = Form(...),
#     manager: StreamingManager = Depends(get_manager(StreamingManager)),
#     ) -> Any:

#     instance = await manager.get(id=name)

#     if not instance:
#         raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,detail="This streaming room is unavailable.")

#     streaming_in = serializers.StreamingUpdate(status=serializers.StreamingStatus.end)
#     instance = serializers.Streaming(**instance)
#     instance = await manager.update(instance=instance,update_object=streaming_in)

#     return Response(serializers.Streaming,data=instance)()


from datetime import datetime
import psutil

HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
ORANGE = '\033[33m'
RED = '\033[31m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
PINK = '\033[95m'

"""
Toma una función como argumento y devuelve una función
wrapper que muestra el uso de la CPU de la función original.
"""
def cpuStats(func):

    def wrapper(*args, **kwargs):
        initial_time = datetime.now()
        func(*args, **kwargs)
        final_time = datetime.now()
        time_elapsed = final_time - initial_time
        print(RED + '*'*50 + ENDC)
        print(f'{WARNING}Tiempo transcurrido: {time_elapsed}')
        print(f'{OKBLUE}Porcentaje CPU: {psutil.cpu_percent()} %')
        print(f'{ORANGE}Estadísticas del CPU: {psutil.cpu_stats()}')
        print(f'{OKCYAN}Memoria RAM: {psutil.virtual_memory().percent} %{ENDC}')
        print(RED + '*'*50 + ENDC)
    return wrapper


import math
import collections
import time
from functools import reduce
from operator import add
import copy
import os
import sys
from itertools import combinations
from pyspark import SparkContext

# os.environ['PYSPARK_PYTHON'] = '/usr/local/bin/python3.6'
# os.environ['PYSPARK_DRIVER_PYTHON'] = '/usr/local/bin/python3.6'

start       = time.time()
case        = 1
support     = 9
input_path  = "./data/small2.csv"
output_path = "./output.txt"

# case = int(sys.argv[1])
# support = int(sys.argv[2])
# input_path = sys.argv[3]
# output_path = sys.argv[4]

sc = SparkContext ('local[*]','task1')

if case == 1:
    small1  = sc.textFile(input_path, 2).map(lambda l: l.split(",")).filter(lambda l: l[0] != 'user_id').map(lambda l: (l[0], l[1])).distinct()
    baskets = small1.groupByKey().map(lambda s: sorted(list(s[1])))

elif case == 2:
    small1  = sc.textFile(input_path, 2).map(lambda l: l.split(",")).filter(lambda l: l[0] != 'user_id').map(lambda l: (l[1], l[0])).distinct()
    baskets = small1.groupByKey().map(lambda s: sorted(list(s[1])))

total_baskets = baskets.count()
buckets       = 99

#genereate new candidate pairs
def gen_new_cand(freq_cand):

    if len(freq_cand)>0 and freq_cand is not None:

        n        = len(freq_cand)
        si       = len(freq_cand[0])
        new_cand = []

        for i in range(n-1):
            for j in range(i+1,n):
                if freq_cand[i][:-1] == freq_cand[j][:-1]:
                    possible_cand = tuple(sorted(list(set(freq_cand[j]).union(set(freq_cand[i])))))
                    temp = []
                    for subset_item in combinations(possible_cand, si):
                        temp.append(subset_item)
                    if set(temp).issubset(set(freq_cand)):
                        new_cand.append(possible_cand)
                else:
                    break
    return new_cand

#find the frequent itemsets
def find_itemsets(subset_baskets, support, total_baskets):

    temp           = list(subset_baskets)
    ps             = math.ceil(support * len(list(temp)) / total_baskets)
    subset_baskets = temp

    subset_baskets = list(subset_baskets)
    candidates_all = collections.defaultdict(list)

    count_dict = collections.defaultdict(int)
    bit_map    = [0 for i in range(buckets+1)]

    for basket in subset_baskets:
        for item in basket:
            count_dict[item]+=1
        for each_pair in combinations(basket, 2):
            key = (int(list(each_pair)[0]) + int(list(each_pair)[1])) % buckets
            bit_map[key] += 1

    single_freq = sorted([k for k in count_dict.keys() if count_dict[k]>= ps])
    bit_map     = list(map(lambda value: True if value >= ps else False, bit_map))

    item_len = 1
    cand     = single_freq

    candidates_all[str(item_len)] = [tuple(item.split(",")) for item in single_freq]

    while len(cand) > 0 and cand is not None:
        item_len  += 1
        count_dict = collections.defaultdict(int)

        for basket in subset_baskets:
            basket = sorted(list(set(basket).intersection(set(single_freq))))

            if len(basket) >= item_len:
                if item_len == 2:
                    for each_pair in combinations(basket, item_len):
                        key = (int(list(each_pair)[0]) + int(list(each_pair)[1])) % buckets
                        if bit_map[key]:
                            count_dict[each_pair] += 1

                if item_len >= 3:
                    for items in cand:
                        if set(items).issubset(set(basket)):
                            count_dict[items]+=1

        freq_cand = sorted([k for k in count_dict.keys() if count_dict[k] >= ps])

        cand = gen_new_cand(freq_cand)
        if len(freq_cand) == 0:
            break

        candidates_all[str(item_len)] = list(freq_cand)

    yield reduce(lambda val1, val2: val1 + val2, candidates_all.values())

#count frequent itemsets
def count_frequent_itemset(subset_baskets, candidates):
    count_dict = collections.defaultdict(int)
    for pairs in candidates:
        if set(pairs).issubset(set(subset_baskets)):
            count_dict[pairs]+=1

    yield [tuple((k, v)) for k, v in count_dict.items()]

def change_format(items):

    result= ""
    idx = 1

    for item in items:
        if len(item) == 1:
            result += str("(" + str(item)[1:-2] + "),")

        elif len(item) != idx:
            result  = result[:-1]
            result  += "\n\n"
            idx     = len(item)
            result  += (str(item) + ",")
        else:
            result += (str(item) + ",")

    return result[:-1]

candidates = baskets.mapPartitions(lambda part: find_itemsets(subset_baskets = part, support = support, total_baskets = total_baskets)).flatMap(lambda pair: pair).distinct()
sorted_candidates = candidates.sortBy(lambda pair: (len(pair), pair)).collect()
frequent_itemsets = baskets.flatMap(lambda part: count_frequent_itemset(subset_baskets = part,candidates = sorted_candidates)).flatMap(lambda pair: pair).reduceByKey(lambda a,b: a + b).filter(lambda a: a[1] >= support).map(lambda a: a[0])
sorted_frequent_itemsets=frequent_itemsets.sortBy(lambda pair: (len(pair), pair)).collect()

with open(output_path, 'w') as op_file:
    str_result = 'Candidates:\n' + change_format(sorted_candidates) + '\n\n' \
                    + 'Frequent Itemsets:\n' + change_format(sorted_frequent_itemsets)
    op_file.write(str_result)
    op_file.close()

end = time.time()
print("Duration: "+ str(end - start))


#!/usr/bin/env python3

"""
Created on Feb 18 2023

PURPOSE:
Removes RFI interference by subtracting a constant pattern in declination, FDEC.
Used for the postprocessing stage of the QUIJOTE MFI wide survey maps (Rubino-Martin et al. 2023).

Based on the original IDL code healpix_remove_mfi_interference_constdec_with_mask.pro used
in the QUIJOTE MFI wide survey pipeline.

@author: jalberto, elenadelahoz
"""

import numpy as np
import healpy as hp
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt

def get_function_constdec_for_onemap(map_in, mask, dec, deltad_arcmin=20.0):
    npix = len(map_in)

    # Copy map and remove bad pixels
    map_out = np.copy(map_in)
    mapa    = np.copy(map_in*mask) # masked
    mapa[mapa==hp.UNSEEN]=0

    # 1) Stack in declination
    cutno0  = np.argwhere(map_out != 0.0)[:,0]
    cutno0m = np.argwhere(mapa != 0.0)[:,0]

    decmin  = min(dec[cutno0m])
    decmax  = max(dec[cutno0m])
    deltad  = deltad_arcmin / 60.0
    ndecs   = int(np.rint( (decmax-decmin)/deltad ) )
    if ndecs == 0: ndecs = 300

    vdec    = np.arange(ndecs) * deltad + decmin

    stack  = np.zeros(ndecs)
    for j in range(ndecs):
       cut = np.argwhere((dec >= (vdec[j]-deltad/2.0)) * (dec < (vdec[j]+deltad/2.0)))[:,0]
       if len(cut) >= 2:
          pixset = mapa[cut]
          ind = np.argwhere(pixset != 0)[:,0]
          if len(ind) >= 1: pixset = pixset[ind]
          if len(pixset) >= 2: stack[j] = np.median(pixset)

    # 2) Project template back onto sky
    template2 = np.zeros(npix)
    if (len(cutno0) >= 1):
       f2 = interp1d(vdec, stack, kind='linear', fill_value="extrapolate")
       cut = np.argwhere((dec >= decmin) * (dec <= decmax))[:,0]
       res = f2(dec[cut])
       if len(cut) >= 1: template2[cut] = res

    # 3) Substract template
    if len(cutno0) >= 1: map_out[cutno0] -= template2[cutno0]

    return map_out

# Remove FDEC from healpix map in intensity only.
def healpix_remove_interference_fdec_int(mapa, maskI,deltad_arcmin=20.0):
    npix  = mapa.shape[-1]
    nside = hp.npix2nside(npix)

    # map with declination information.
    ipix           = np.arange(npix)
    theta, phi     = hp.pix2ang(nside, ipix, nest=False)
    r              = hp.Rotator(coord=['G','C'])
    theta_c, phi_c = r(theta, phi)
    dec            = 90.0 - np.rad2deg(theta_c)

    # Correcting intensity maps.
    print(" --> Computing FDEC for intensity map: ")
    map_out = get_function_constdec_for_onemap(mapa, maskI, dec,deltad_arcmin=deltad_arcmin)

    return map_out

# Rotate Stokes Q-U maps G-->C.
def rotate_stokes_healpixmap(Q_in, U_in, gal2eq=False, eq2gal=False):

    if gal2eq: signo= 1.
    if eq2gal: signo=-1.

    npix       = Q_in.shape[-1]
    nside      = hp.npix2nside(npix)
    ipix       = np.arange(npix)
    r          = hp.Rotator(coord=['G','C'])
    theta, phi = hp.pix2ang(nside, ipix, nest=False)
    ang        = r.angle_ref(theta,phi)

    Q_out =  Q_in*np.cos(2.*ang) - U_in*np.sin(2.*ang*signo)
    U_out =  Q_in*np.sin(2.*ang*signo) + U_in*np.cos(2.*ang)

    return Q_out, U_out

# Remove FDEC for healpix map in intensity and polarization
def healpix_remove_interference_fdec_pol(mapa, maskI, maskP,deltad_arcmin=20.0):
    npix  = mapa.shape[-1]
    nside = hp.npix2nside(npix)

    # map with declination information.
    ipix           = np.arange(npix)
    theta, phi     = hp.pix2ang(nside, ipix, nest=False)
    r              = hp.Rotator(coord=['G','C'])
    theta_c, phi_c = r(theta, phi)
    dec            = 90.0 - np.rad2deg(theta_c)

    # Correcting intensity maps.
    print(" --> Computing FDEC for the intensity map: ")
    mapI_out = get_function_constdec_for_onemap(mapa[0], maskI, dec, deltad_arcmin=deltad_arcmin)

    # Correcting polarization maps.
    print(" --> Computing FDEC for the polarization maps: ")
    mapQ = np.copy(mapa[1])
    mapU = np.copy(mapa[2])

    mapQr, mapUr = rotate_stokes_healpixmap(mapQ, mapU, gal2eq=True)

    mapQ_out = get_function_constdec_for_onemap(mapQr, maskP, dec, deltad_arcmin=deltad_arcmin)
    mapU_out = get_function_constdec_for_onemap(mapUr, maskP, dec, deltad_arcmin=deltad_arcmin)

    mapQfinal, mapUfinal = rotate_stokes_healpixmap(mapQ_out, mapU_out, eq2gal=True)

    map_out = np.asarray([ mapI_out, mapQfinal, mapUfinal ])

    return map_out

# Run anafast for intensity map only
def run_anafast(m1, masc):
    npix  = m1.shape[-1]
    nside = hp.npix2nside(npix)
    lmax  = 2*nside
    m1m   = np.copy(m1)
    for j in np.arange(3): m1m[j,:] = m1[j,:]*masc[:]
    cl    = hp.anafast(m1m, lmax=lmax, pol=True)
    fsky  = np.sum(masc)/float(npix)
    ell   = np.arange(lmax + 1)
    cl   *= 1.0 / fsky  # approximately corrects for sky mask
    return ell, cl

# MAIN code
if __name__ == "__main__":

    # Paths
    path_wmap    = '/Users/jalberto/WMAP/WMAP9/'
    path_quijote = '../'

    # Global params
    nside = 512
    npix  = hp.nside2npix(nside)
    # Width of declination bins, in arcmin. QUIJOTE MFI used 20arcmin = 1/3 of FWHM for 1deg, and nside=512.
    deltad_arcmin = 20.0

    # FDEC in polarization
    # WMAP K-band map
    ff    = path_wmap + 'wmap_band_iqumap_r9_9yr_K_v5.fits'
    mapa  = hp.read_map(ff,field=[0,1,2],nest=False)
    nmaps = mapa.shape[0]

    # Masks.
    maskI = hp.read_map(path_quijote+'fdec/quijote_mask_fdec_int_nside512_ring.fits',nest=False)
    maskP = hp.read_map(path_quijote+'fdec/quijote_mask_fdec_pol_nside512_ring.fits',nest=False)
    masc  = hp.read_map(path_quijote+'masks/mask_quijote_ncp_lowdec_satband_nside512.fits',nest=False)
    mascapo = hp.smoothing(masc, fwhm=np.radians(5.0))

    # Apply FDEC
    map_out = healpix_remove_interference_fdec_pol(mapa, maskI, maskP, deltad_arcmin=deltad_arcmin)

    # Display maps
    qmax = np.max(mapa[1])
    qmin = np.min(mapa[1])
    tmax = np.max(mapa[0])
    hp.mollview(mapa[1], norm='hist',title="Q Map in (orig)",sub=(2,2,1),max=qmax,min=qmin)
    hp.mollview(map_out[1], norm='hist',title="Q FDEC corrected",sub=(2,2,2),max=qmax,min=qmin)
    hp.mollview(mapa[1]-map_out[1], norm='hist', title='Q FDEC',sub=(2,2,3),max=qmax,min=qmin)
    hp.mollview(mapa[0]-map_out[0], norm='hist', title='I FDEC',sub=(2,2,4),max=tmax,min=-tmax)
    #hp.mollview(mascapo,title='Standard QUIJOTE mask (apo)',sub=(2,2,4))
    plt.show()

    # Compute and plot power spectra.
    ell, cl_in   = run_anafast(mapa, mascapo)
    ell, cl_out  = run_anafast(map_out, mascapo)
    ell, cl_fdec = run_anafast(mapa-map_out, mascapo)

    plt.plot(ell,cl_in[0],label='TT input map')
    plt.plot(ell,cl_out[0],label='TT filtered map',linestyle='dashed')
    plt.plot(ell,cl_fdec[0],label='TT FDEC',linestyle='dotted')
    plt.plot(ell,cl_in[1],label='EE input map')
    plt.plot(ell,cl_out[1],label='EE filtered map',linestyle='dashed')
    plt.plot(ell,cl_fdec[1],label='EE FDEC',linestyle='dotted')
    plt.xscale('log')
    plt.yscale('log')
    plt.xlim([10,500])
    plt.xlabel(r'$\ell$', fontsize=16)
    plt.ylabel(r'$C_\ell$ ' , fontsize=16)
    plt.title(r'Spectra')
    plt.legend(loc='upper right', ncol=2, labelspacing=0.1)
    plt.show()

    # Transfer function.
    tf = 1./(1. - cl_fdec/cl_out)
    tf[np.isnan(tf)] = 1. # correct l=0 and l=1
    dl  = 11
    box = np.ones(dl)/dl

    plt.plot(ell,np.convolve(tf[0],box,mode='same'),label='TT')
    plt.plot(ell,np.convolve(tf[1],box,mode='same'),label='EE')
    plt.plot(ell,np.convolve(tf[2],box,mode='same'),label='BB')
    plt.xscale('log')
    plt.yscale('linear')
    plt.xlim([10,200])
    plt.ylim([0.9,1.2])
    plt.xlabel(r'$\ell$', fontsize=16)
    plt.ylabel(r'$f_\ell$ ' , fontsize=16)
    plt.title(r'Transfer function, $\Delta Dec=$'+str(deltad_arcmin)+' arcmin.')
    plt.legend(loc='upper right', ncol=2, labelspacing=0.1)
    #plt.savefig("test_linear.png",dpi=300, bbox_inches="tight")
    plt.show()