import Manyo
import Manyo.MLF as mm
import math
import numpy as np
from uGao.uGaoUtil import IFEvtProp, MASKVALUE, MIN_VALUE, GetFontProp, PlotException
from uGao.uGaoColorMap import COLOR_MAPS_TITLE, DEFAULT_MAP, GMT_HAXBY_MAP, TURBO_MAP, COLOR_MAPS, COLOR_MAPS_TITLE
from pylab import setp, getp, Normalize, Rectangle
from matplotlib import lines
import logging
import matplotlib
from matplotlib.colors import (LogNorm, )
try:
    # this causes error in matplotlib v3.9.0
    from matplotlib.cm import (get_cmap)
except:
    from matplotlib.pylab import (get_cmap)
from matplotlib.patches import (Circle, Wedge, Polygon)
from matplotlib.collections import (QuadMesh)
from matplotlib.ticker import (AutoMinorLocator, StrMethodFormatter)
import scipy
import scipy.signal
from matplotlib import __version__ as mpl_version

'''
Moved methods
from u2dplot
- ScaleParams
- U2Params
- U2DChart
from U2IF
- DataConverter
'''
# マスク値の色定義
BAD_COLORS = ['w', 'w', 'w', 'c', 'w', 'c', 'w']
# ドラッグカーソルの色定義
CURSOL_COLORS = ['r', 'k', 'r', 'r', 'r', 'r', 'r']
# 描画用色定義
MCOLORS = ['w', 'y', 'r', 'k', 'b', 'g', 'c', 'm']

#######################################
#  ScaleParams
#######################################

class ScaleParams(object):
    """
     スケールタブクラス
    """
    tick_formats = ["Floating Point", "Exponent"]

    #########################################
    def __init__(self, label):
        """
        コンストラクタ
        @param  label (string) 軸ラベル
        @retval 無し
        """
        # 自動範囲
        self.autoRange = True
        # 範囲
        self.range = None
        # 軸ラベル
        self.label = label
        # Unit
        self.unit = ""
        # 自動チック
        self.autoTick = True
        # チックラベルの表示形式 0:浮動小数点  1:指数形式
        self.format = 0
        # 精度
        self.precision = 3
        # ログスケール
        self.logScale = False
        # 軸反転
        self.inversion = False

    #########################################
    def __repr__(self):
        """
        文字列出力用メソッド
        """
        if self.format == 0:
            str_format = "Floating Point"
        else:
            str_format = "Exponent"
        if self.range is None:
            str_range = "None"
        elif isinstance(self.range, list):
            str_range = "{0:g} - {1:g}".format(*self.range)
        else:
            str_range = "None"
        s = """\
Label: %s
Unit: %s
Auto Range: %s
Range: %s
Log Scale: %s
Inversion: %s
Auto Tick: %s
Tick Format: %s
Tick Precision: %d
""" % (self.label, self.unit, self.autoRange, str_range, self.logScale, self.inversion, self.autoTick,
            self.tick_formats[self.format], self.precision)
        return s

#######################################
#  U2Params
#######################################
class U2Params(object):
    """
     uGao パラメータクラス
    """
    grid_colors = ["White", "Yellow", "Red",
                   "Black", "Blue", "Green", "Cyan", "Purple"]
    # cmap_types = ["Default", "Red Scale", "Blue Scale", "Gray Scale", "GMT Haxby"]
    cmap_types = COLOR_MAPS_TITLE
    REG_COLORS = ["White", "Yellow", "Red", "Black",
                  "Blue", "Green", "Cyan", "Purple"]  # 領域色選択肢
    font_types = ["Serif", "Sans-serif", "Monospace"]

    #########################################
    def __init__(self):
        """コンストラクタ
        """
        # グラフタイトル
        self.title = ""
        # グラフコメント
        self.comment = ""
        # 自動更新指定
        self.autoUpdate = True
        # X軸とY軸の倒置指定
        self.transPosition = False
        # Xスケールパラメータクラス
        self.xscale = ScaleParams("X")
        # Yスケールパラメータクラス
        self.yscale = ScaleParams("Y")
        # グリッド線の色指定  0:White, 1:Yellow 2:Red, 3:Black, 4:Blue, 5:Green, 6:Cyan, 7:Purple
        self.gridColorH = 4
        self.gridColorV = 4
        # グリッド線の太さ指定
        self.gridWidthH = "0.5"
        self.gridWidthV = "0.5"
        # グリッド線種指定
        self.gridStyleH = "Dotted"
        self.gridStyleV = "Dotted"
        # 水平グリッド表示指定
        self.hgrid = False
        # 水平グリッドの間隔
        self.hgridInterval = None
        # 垂直グリッド表示指定
        self.vgrid = False
        # 垂直グリッドの間隔
        self.vgridInterval = None
        # カラーマップ  0:defaule, 1:Red Scale, 2:Blue Scale, 3:Gray Scale
        self.colorMap = 0
        # 自動範囲
        self.autoRange = True
        # ログスケール
        self.logScale = False
        # 強度範囲
        self.range = None
        # 強度(%)
        self.relRange = 100
        #
        self.groups = ['group1', 'group2']
        #
        self.colors = [0, 1]
        # DetectorMap mode
        self.detectmapmode = False
        # バンクセパレータ
        self.bankseparator = False
        # Smoothing
        self.smoothing = [False, 1, 1]
        # ウィンドウサイズ
        # self.window_size = [None, None]
        self.window_size = [435, 464]
        # Mouse Dragging path
        self.mouseDragPath = [[0.0, 0.0], [0.0, 0.0]]
        self.diagonalSliceInfo = [1.0, 1.0]  # [ width, binning ]
        # Slice mode
        self.slice_ave_mode = True  # average or summation
        # Tick Style Dictionary
        self.TickStyleDic = {"Minor": False,
                             "MinorLength": 2,  # default=2
                             "MinorWidth": 0.5,  # default=0.5
                             "MajorLength": 5,  # default=5
                             "MajorWidth": 0.5,  # default=0.5
                             "BottomSide": True,
                             "LeftSide": True,
                             "TopSide": False,
                             "RightSide": False,
                             "BadColor": "white",
                             "BGColor": "white",
                             "TickColor": "black",
                             "FrameLineWidth": 1.0}
        # Font Dictionary
        self.fontDict = {"title": [10, 'Serif'],
                         "comment": [8, 'Serif'],
                         "x": [9, 'Serif'],
                         "y": [9, 'Serif'],
                         "xticks": [8, 'Serif'],
                         "yticks": [8, 'Serif'],
                         "cbticks": [6, 'Serif']}

    #######################################
    def __repr__(self):
        """
        文字列出力用メソッド
        """
        if self.range is None:
            str_range = "None"
        elif isinstance(self.range, list):
            str_range = "{:g} - {:g}".format(*self.range)
        else:
            str_range = "None"
        s = "Title: {}\n".format(self.title)
        s += "Comment: {}\n".format(self.comment)
        s += "Auto Update: {}\n".format(self.autoUpdate)
        s += "X Scale:\n {}\n".format(self.xscale)
        s += "Y Scale:\n {}\n".format(self.yscale)
        s += "Horizontal Grid: {}\n".format(self.hgrid)
        s += "Horizontal Grid Color: {}\n".format(
            self.grid_colors[self.gridColorH])
        s += "Horizontal Grid Width: {}\n".format(self.gridWidthH)
        s += "Horizontal Grid Style: {}\n".format(self.gridStyleH)
        s += "Horizontal Grid Interval: {}\n".format(self.hgridInterval)
        s += "Vertical Grid: {}\n".format(self.vgrid)
        s += "Vertical Grid Color: {}\n".format(
            self.grid_colors[self.gridColorV])
        s += "Vertical Grid Width: {}\n".format(self.gridWidthV)
        s += "Vertical Grid Style: {}\n".format(self.gridStyleV)
        s += "Vertical Grid Interval: {}\n".format(self.vgridInterval)
        s += "Color Map: {}\n".format(self.cmap_types[self.colorMap])
        s += "Auto Range: {}\n".format(self.autoRange)
        s += "Log Scale: {}\n".format(self.logScale)
        s += "Intensity Range: {}\n".format(self.range)
        s += "Intensity Relative: {}\n".format(self.relRange)
        s += "Groups: {}\n".format(self.groups)
        s += "Colors: {}\n".format(self.colors)
        s += "Window Size: {}\n".format(self.window_size)
        s += "Tick Style:\n"
        s += "   Major Ticks Length: {}\n".format(
            self.TickStyleDic["MajorLength"])
        s += "   Major Ticks Width: {}\n".format(
            self.TickStyleDic["MajorWidth"])
        s += "   Minor Ticks: {}\n".format(self.TickStyleDic["Minor"])
        s += "        Length: {}\n".format(self.TickStyleDic["MinorLength"])
        s += "        Width : {}\n".format(self.TickStyleDic["MinorWidth"])
        s += "   Top Side Tick   : {}\n".format(self.TickStyleDic["TopSide"])
        s += "   Bottom Side Tick: {}\n".format(
            self.TickStyleDic["BottomSide"])
        s += "   Left Side Tick  : {}\n".format(self.TickStyleDic["LeftSide"])
        s += "   Right Side Tick : {}\n".format(self.TickStyleDic["RightSide"])
        s += "   FrameLine Width : {}\n".format(
            self.TickStyleDic["FrameLineWidth"])

        return s

#######################################
#  D2Chart
#######################################
class D2Chart(object):
    """
    グラフ描画 Class
    """

    #########################################
    def __init__(self, parent, panel, data, isDarkMode=False):
        """
        コンストラクタ
        @param  parent  起動クラスインスタンス
        @param  panel   描画用パネル
        @param  data (list(array, array,array) ) 表示データ
        @param  isDarkMode  (bool) プロッタ非表示モードか？
        @retval 無し
        """
        # Draw managements
        # default_figsize = (7.82, 7.06)
        # Inital figure shape should be regular square
        default_figsize = (7.00, 7.00)
        sModeDic = {}
        mModeDic = {}
        # sModeDic["MainPosi"] = [0.13, 0.105, 0.75, 0.75]
        # mModeDic["MainPosi"] = [0.1, 0.095, 0.62, 0.62]
        # Figure shape must be regular square as initial setting.
        sModeDic["MainPosi"] = [0.1, 0.1, 0.75, 0.75]
        # Figure shape must be regular square as initial setting.
        mModeDic["MainPosi"] = [0.1, 0.1, 0.62, 0.62]
        sModeDic["ColorBarPosi"] = [0.90, 0.105, 0.05, 0.75]
        mModeDic["ColorBarPosi"] = [0.02, 0.04, 0.28, 0.02]
        sModeDic["ColorBarOri"] = 'vertical'
        mModeDic["ColorBarOri"] = 'horizontal'
        sModeDic["TickLabelSize"] = 10
        mModeDic["TickLabelSize"] = 8
        sModeDic["LabelSize"] = 12
        mModeDic["LabelSize"] = 8
        sModeDic["TitleSize"] = 12
        mModeDic["TitleSize"] = 12
        sModeDic["LabelFont"] = 'serif'
        mModeDic["LabelFont"] = 'serif'
        sModeDic["TitleFont"] = 'serif'
        mModeDic["TitleFont"] = 'serif'
        sModeDic["ScaleFont"] = 'serif'
        mModeDic["ScaleFont"] = 'serif'
        # Padding: (left, bottom, right, top) in inch
        sModeDic["Padding"] = (default_figsize[0] * sModeDic["MainPosi"][0],
                               default_figsize[1] * sModeDic["MainPosi"][1],
                               default_figsize[0] * (1.0 - sModeDic["MainPosi"]
                                                     [0] - sModeDic["MainPosi"][2] - 0.07),
                               default_figsize[1] * (1.0 - sModeDic["MainPosi"][1] - sModeDic["MainPosi"][3]))
        mModeDic["Padding"] = (default_figsize[0] * mModeDic["MainPosi"][0],
                               default_figsize[1] * mModeDic["MainPosi"][1],
                               default_figsize[0] * (1.0 - mModeDic["MainPosi"]
                                                     [0] - mModeDic["MainPosi"][2] - 0.23),
                               default_figsize[1] * (1.0 - mModeDic["MainPosi"][1] - mModeDic["MainPosi"][3] - 0.23))
        # Gap: (x, y) in inch
        sModeDic["Gap"] = (default_figsize[0] * 0.02, 0.0)
        mModeDic["Gap"] = (default_figsize[0] * 0.05,
                           default_figsize[1] * 0.05)
        # MainSize: (width, height) of main axes in inch
        sModeDic["MainSize"] = (default_figsize[0] * sModeDic["MainPosi"][2],
                                default_figsize[1] * sModeDic["MainPosi"][3])
        mModeDic["MainSize"] = (default_figsize[0] * mModeDic["MainPosi"][2],
                                default_figsize[1] * mModeDic["MainPosi"][3])
        # SubSize: (width of colorbar, height of colorbar) in inch for single
        # SubSize: (width of right, height of top) in inch for multi
        sModeDic["SubSize"] = (default_figsize[0] * sModeDic["ColorBarPosi"][2], 0.0)
        mModeDic["SubSize"] = (default_figsize[0] * 0.18,
                               default_figsize[1] * 0.18)
        fDic = ["serif", "sans-serif", "monospace"]
        self.DrawDic = {"SingleMode": sModeDic,
                        "MultiMode": mModeDic, "FontList": fDic}

        self.parent = parent
        self.fig = parent.fig
        self.canvas = parent.canvas
        self.cid_draw = self.canvas.mpl_connect('draw_event', self.on_draw)
        self.transPosi = False
        # プロパティ管理クラスのインスタンスを取得
        self.ifi = IFEvtProp(parent.order)
        self.regInfos = []
        # 領域情報の公開プロパティを登録
        self.ifi.AddProperty(
            'region_info', lambda: self.regInfos)  # [20170206, TI]

        # メッセージのリスナー登録
        self.ifi.AddListner('change_params', self.OnNotifyChangePrms)
        self.ifi.AddListner('redraw_regions', self.OnNotifyRedrawRegions)
        self.ifi.AddListner('draw_slice_region', self.OnNotifyDrawSliceRegion)
        self.ifi.AddListner('overplot', self.OnNotifyOverPlot)  # Over Plot

        # デフォルトカラーマップの作成
        self.colormap_default = matplotlib.colors.LinearSegmentedColormap(
            'default_utsusemi', DEFAULT_MAP, 256)
        self.colormap_GMThaxby = matplotlib.colors.LinearSegmentedColormap(
            'GMT_haxby', GMT_HAXBY_MAP, 256)
        self.colormap_Turbo = matplotlib.colors.LinearSegmentedColormap(
            'turbo', TURBO_MAP, 256)

        # 描画パラメータの取得
        self.params = parent.params

        # プロッタ非表示モードか
        self.isDarkMode = isDarkMode

        # データの初期化
        self.InitData(data)

        # 描画
        self.PlotD2Chart()

    #########################################
    def InitData(self, data):
        """
        データの初期化
        @param  data (numpy array のタプル)  (Int,X,Y)
        @retval 無し
        """
        # データを格納
        self.Z = data[0]
        self.X = data[1]
        self.Y = data[2]
        # マスク値を除く最大値を検索
        self.maxCount = 0.0
        for i in range(len(self.Z)):
            for j in range(len(self.Z[0])):
                if self.Z[i][j] < MASKVALUE:
                    if self.Z[i][j] > self.maxCount:
                        self.maxCount = self.Z[i][j]

        # マップデータからマスクデータ作成
        self.Zm = np.ma.masked_where(self.Z >= MASKVALUE, self.Z)

        # X軸描画範囲の初期設定
        self.xrange = (self.X.min(), self.X.max())
        if self.params.xscale.autoRange:
            self.params.xscale.range = self.xrange

        # Y軸描画範囲の初期設定
        self.yrange = (self.Y.min(), self.Y.max())
        if self.params.yscale.autoRange:
            self.params.yscale.range = self.yrange

        # Z軸描画範囲の初期設定
        self.zMin = self.Z.min()
        self.zMax = self.maxCount
        if self.params.autoRange:
            if self.isDarkMode:
                if self.params.range is None:
                    self.params.range = (self.zMin, self.zMax)
            else:
                self.params.range = (self.zMin, self.zMax)

    #########################################
    def GetDefaultWidth(self):
        """
        幅の初期値を取得
        @param   無し
        @retval 無し
        """
        x0, x1 = self.ax.get_xlim()
        y0, y1 = self.ax.get_ylim()

        x2 = (x1 - x0) * (x1 - x0)
        y2 = (y1 - y0) * (y1 - y0)
        gwidth = math.sqrt(x2 + y2)
        return gwidth / 10.0

    #########################################
    def ApplySmoothing(self):
        """
        スムージングを行う。しない場合はマスクだけかける
        @param  None
        @retval None
        """
        if self.params.smoothing[0]:
            sw_org = int(abs(self.params.smoothing[1]))
            sm_times = 1
            if len(self.params.smoothing) > 2:
                sm_times = int(self.params.smoothing[2])
            sw = int(sw_org * 2)
            numZx = int(self.Z.size / self.Z[0].size)
            numZy = int(self.Z[0].size)
            outZm = self.Z.copy()
            for loop in range(sm_times):
                Zm = np.zeros(
                    (int(numZx + sw * 2), int(numZy + sw * 2)), float)

                for i in range(numZx):
                    for j in range(numZy):
                        # Zm[sw+i][sw+j]=self.Z[i][j]
                        Zm[sw + i][sw + j] = outZm[i][j]

                mX, mY = np.where(self.Z >= (MASKVALUE - 1.0))
                for (i, j) in zip(mX, mY):
                    st_k = i - sw
                    en_k = i + sw
                    st_l = j - sw
                    en_l = j + sw
                    if st_k < 0:
                        st_k = 0
                    if en_k >= numZx:
                        en_k = numZx - 1
                    if st_l < 0:
                        st_l = 0
                    if en_l >= numZy:
                        en_l = numZy - 1

                    ssum = 0.0
                    isum = 0
                    for k in range(st_k, en_k + 1):
                        for l in range(st_l, en_l + 1):
                            if self.Z[k][l] < MASKVALUE:
                                isum += 1
                                ssum += self.Z[k][l]

                    if isum != 0:
                        Zm[sw + i][sw + j] = ssum / float(isum)

                for i in range(numZx):
                    for j in range(sw):
                        Zm[sw + i][j] = (Zm[sw + i][sw] + Zm[sw + i][sw + 1]) / 2.0
                        Zm[sw + i][j + numZy] = (Zm[sw + i][sw + numZy - 2] + Zm[sw + i][sw + numZy - 1]) / 2.0

                for i in range(sw):
                    for j in range(numZy + sw * 2):
                        Zm[i][j] = (Zm[sw][j] + Zm[sw + 1][j]) / 2.0
                        Zm[numZx + i][j] = (Zm[numZx - 2][j] + Zm[numZx - 1][j]) / 2.0

                g = self.gauss_kern(sw_org, None)
                if scipy.__version__ > '0.19.0':
                    tmpZm = scipy.signal.convolve(
                        Zm, g, mode='valid', method='direct')
                else:
                    tmpZm = scipy.signal.convolve(Zm, g, mode='valid')
                outZm = np.zeros((numZx, numZy), float)
                for i in range(numZx):
                    for j in range(numZy):
                        outZm[i][j] = tmpZm[sw_org + i][sw_org + j]

            if outZm.size == self.Z.size:
                self.Zm = np.ma.masked_where(self.Z >= MASKVALUE - 1.0, outZm)
            else:
                print("self.Z.size/self.Z[0].size= {}".format(numZx))
                print("self.Z[0].size = {}".format(numZy))
                self.Zm = np.ma.masked_where(self.Z >= MASKVALUE - 1.0, self.Z)
        else:
            self.Zm = np.ma.masked_where(self.Z >= MASKVALUE - 1.0, self.Z)

        return

    #########################################
    def gauss_kern(self, size, sizey=None):
        size = int(size)
        if not sizey:
            sizey = size
        else:
            sizey = int(sizey)
        x, y = np.mgrid[-size:size + 1, -sizey:sizey + 1]
        g = np.exp(-(x ** 2 / float(size) + y ** 2 / float(sizey)))
        return g / g.sum()

    #########################################
    def PlotD2Chart(self):
        """
        2Dチャートを描画する
        @param   無し
        @retval 無し
        """
        logging.debug("D2Chart::PlotD2Chart")
        sliceMode = self.ifi.GetProperty("slice_mode")
        # 全グラフクリア
        self.fig.clf()
        # フォントのプロパティを取得
        prop = GetFontProp()

        # 軸の指定が変わったか
        if self.params.transPosition:
            yy = self.X
            xx = self.Y
        else:
            xx = self.X
            yy = self.Y
        title = self.params.title
        comment = self.params.comment
        # スライダーの値から割合を取得
        ratio = self.params.relRange * 1.0 / 100.0

        if sliceMode:
            target = self.DrawDic["MultiMode"]
        else:
            target = self.DrawDic["SingleMode"]
        _main_posi = target["MainPosi"]
        # マップ用のサブプロットを準備
        self.ax = self.fig.add_subplot(111)
        self.ax.set_position(_main_posi)
        self.ax.tick_params(direction="in")

        # カラーバー用の座標軸を準備
        self.axc = self.fig.add_axes(target["ColorBarPosi"])

        # 領域表示用のサブプロットを準備(透過モード)
        self.regAxes = self.fig.add_subplot(111, frameon=False)
        self.regAxes.set_position(target["MainPosi"])
        self.regAxes.tick_params(direction="in")

        # Add sharex, sharey @250410 -> remove @150420
        # self.regAxes.sharex(self.ax)
        # self.regAxes.sharey(self.ax)

        # スライス表示用のサブプロットを準備(透過モード)
        self.sliceAxes = self.regAxes  # 領域表示と同じインスタンスを使用。続く3行は不要になった。
        # self.sliceAxes = self.fig.add_subplot(111, frameon=False)
        # self.sliceAxes.set_position(target["MainPosi"])
        # self.sliceAxes.tick_params(direction="in")

        if sliceMode:
            self.ax_top = self.fig.add_axes([_main_posi[0], _main_posi[1] + _main_posi[3] + 0.05, _main_posi[2], 0.18],
                                            gid="ax_top")
            self.ax_top.locator_params(axis="y", nbins=3)
            setp(self.ax_top.get_xticklabels(), size=6),
            setp(self.ax_top.get_yticklabels(), size=6),
            self.ax_top.tick_params(direction="in")
            self.ax_right = self.fig.add_axes(
                [_main_posi[0] + _main_posi[2] + 0.05, _main_posi[1], 0.18, _main_posi[3]], gid="ax_right")
            self.ax_right.locator_params(axis="x", nbins=3)
            setp(self.ax_right.get_xticklabels(), size=6),
            setp(self.ax_right.get_yticklabels(), size=6),
            self.ax_right.tick_params(direction="in")

        # オーバプロット用のサブプロットを準備(透過モード)
        self.ovplot = self.regAxes  # 領域表示と同じインスタンスを使用。続く3行は不要になった。
        # self.ovplot = self.fig.add_subplot(111, frameon=False)
        # self.ovplot.set_position(target["MainPosi"])
        # self.ovplot.tick_params(direction="in")

        # デフォルト指定であれば
        if self.params.colorMap == 0:
            # オリジナルのマップを設定
            cmap = self.colormap_default
        elif COLOR_MAPS[self.params.colorMap] == 'haxby':
            cmap = self.colormap_GMThaxby
        elif COLOR_MAPS[self.params.colorMap] == 'turbo':
            cmap = self.colormap_Turbo
        else:
            cmap = get_cmap(COLOR_MAPS[self.params.colorMap])

        # マスク値の色を設定
        # cmap.set_bad(BAD_COLORS[self.params.colorMap], 1.0)
        cmap.set_bad(self.params.TickStyleDic["BadColor"], 1.0)
        if matplotlib.__version__ < '2.0.0':
            self.ax.set_axis_bgcolor(self.params.TickStyleDic["BGColor"])
        else:
            self.ax.set_facecolor(self.params.TickStyleDic["BGColor"])

        # 自動スケールか
        if self.params.autoRange:
            # データの最小・最大値を範囲とする
            zmin = self.zMin
            zmax = self.zMax
            if self.isDarkMode:
                if self.params.range is not None:
                    if self.params.range[0] is not None:
                        zmin = float(self.params.range[0])
                    elif self.params.range[1] is not None:
                        zmax = float(self.params.range[1])
        else:
            # 指定された範囲
            # 与えられた値がNoneでなければその値で置き換え
            if self.params.range[0] is None:
                zmin = self.zMin
            else:
                zmin = float(self.params.range[0])
            if self.params.range[1] is None:
                zmax = self.zMax
            else:
                zmax = float(self.params.range[1])
        # スライダーの設定を反映する
        zmax = (zmax - zmin) * ratio + zmin

        # Smoothing
        self.ApplySmoothing()

        # ログモードか
        if self.params.logScale:
            # ログモードでは0は許容されないので、最小値のオフセットをつける
            logZm = self.Zm + MIN_VALUE
            # 表示データの最小値以下か
            if zmin < MIN_VALUE:
                zmin = MIN_VALUE
            pc = self.ax.pcolormesh(xx, yy, logZm, cmap=cmap,
                                    norm=LogNorm(vmin=zmin, vmax=zmax))
        # リニアモード
        else:
            pc = self.ax.pcolormesh(xx, yy, self.Zm, cmap=cmap,
                                    norm=Normalize(vmin=zmin, vmax=zmax))
        # カラーマップと関連付けて、カラーバーを表示
        self.fig.colorbar(pc, cax=self.axc, orientation=target["ColorBarOri"])
        self.axc.yaxis.set_tick_params(
            which='major', left=True, direction='in')

        # スケールバーの座標ラベルの文字を小さくする
        _prop_cbticks = prop.copy()
        _prop_cbticks.set_size(self.params.fontDict['cbticks'][0])
        _prop_cbticks.set_name(self.params.fontDict['cbticks'][1])
        xlabels = getp(self.axc, 'xticklabels')
        setp(xlabels, fontproperties=_prop_cbticks)
        ylabels = getp(self.axc, 'yticklabels')  # [inamura 120211]
        setp(ylabels, fontproperties=_prop_cbticks)
        # 軸の転置が指定されているか
        if self.params.transPosition:
            y_range = self.xrange
            x_range = self.yrange
            # X軸作成
            self._MakeXScale(self.params.yscale, self.yrange, prop)
            # Y軸作成
            self._MakeYScale(self.params.xscale, self.xrange, prop)
        else:
            x_range = self.xrange
            y_range = self.yrange
            # X軸作成
            self._MakeXScale(self.params.xscale, self.xrange, prop)
            # Y軸作成
            self._MakeYScale(self.params.yscale, self.yrange, prop)

        # 領域のスケールをカラーマップのスケールに合わせる
        self.regAxes.set_xlim(self.ax.get_xlim())
        self.regAxes.set_ylim(self.ax.get_ylim())
        # 領域スケールのラベルを消す
        self.regAxes.set_xticks([])
        self.regAxes.set_yticks([])
        # If DetectMap mode, remove X ticks
        if self.params.detectmapmode:
            self.ax.set_xticks([])

        # グリッド線のカラーを取得
        gcolorh = MCOLORS[self.params.gridColorH]
        gcolorv = MCOLORS[self.params.gridColorV]

        # 水平グリッド線が指定されていれば表示
        if self.params.hgrid:

            # 間隔が指定されていなければ
            if self.params.hgridInterval is None:
                self.ax.yaxis.grid(True, color=gcolorh,
                                   linestyle=self.params.gridStyleH.lower(),
                                   linewidth=float(self.params.gridWidthH))
            else:
                g0 = int(y_range[0] / self.params.hgridInterval) * \
                    self.params.hgridInterval
                while (g0 < y_range[1]):
                    if g0 > y_range[0]:
                        self.ax.axhline(y=g0, color=gcolorh,
                                        linestyle=self.params.gridStyleH.lower(),
                                        linewidth=float(self.params.gridWidthH))
                    g0 += self.params.hgridInterval
        # 垂直グリッド線が指定されていれば表示
        if self.params.vgrid:

            # 間隔が指定されていなければ
            if self.params.vgridInterval is None:
                self.ax.xaxis.grid(True, color=gcolorv,
                                   linestyle=self.params.gridStyleV.lower(),
                                   linewidth=float(self.params.gridWidthV))
            else:
                g0 = int(x_range[0] / self.params.vgridInterval) * \
                    self.params.vgridInterval
                while (g0 < x_range[1]):
                    if g0 > x_range[0]:
                        self.ax.axvline(x=g0, color=gcolorv,
                                        linestyle=self.params.gridStyleV.lower(),
                                        linewidth=float(self.params.gridWidthV))
                    g0 += self.params.vgridInterval

        if self.params.detectmapmode and self.params.bankseparator:
            separators = self.ifi.GetProperty('bank_separators')
            if separators is None:
                logging.info("No bank separators")
            else:
                for separator in separators:
                    # self.ax.axvline(x=separator+0.5, linestyle='dotted', color=gcolor, linewidth = 0.5)
                    self.ax.axvline(
                        x=separator + 0.5, linestyle='solid', color='black', linewidth=1.0)

        # Tick, Frame Line Settings
        tcolor = self.params.TickStyleDic["TickColor"]
        tick_t = self.params.TickStyleDic["TopSide"]
        tick_b = self.params.TickStyleDic["BottomSide"]
        tick_l = self.params.TickStyleDic["LeftSide"]
        tick_r = self.params.TickStyleDic["RightSide"]
        if mpl_version < '3.0.0':
            tick_side_dic = {True: "on", False: "off"}
            tick_t = tick_side_dic[tick_t]
            tick_b = tick_side_dic[tick_b]
            tick_l = tick_side_dic[tick_l]
            tick_r = tick_side_dic[tick_r]
        self.ax.xaxis.set_tick_params(which='major', length=self.params.TickStyleDic["MajorLength"],
                                      width=self.params.TickStyleDic["MajorWidth"],
                                      bottom=tick_b, top=tick_t, color=tcolor, direction="in")
        self.ax.yaxis.set_tick_params(which='major', length=self.params.TickStyleDic["MajorLength"],
                                      width=self.params.TickStyleDic["MajorWidth"],
                                      left=tick_l, right=tick_r, color=tcolor, direction="in")
        if (self.params.TickStyleDic["Minor"]):
            self.ax.xaxis.set_minor_locator(AutoMinorLocator(5))
            self.ax.yaxis.set_minor_locator(AutoMinorLocator(5))
            self.ax.xaxis.set_tick_params(which='minor', length=self.params.TickStyleDic["MinorLength"],
                                          width=self.params.TickStyleDic["MinorWidth"],
                                          bottom=tick_b, top=tick_t, color=tcolor, direction="in")
            self.ax.yaxis.set_tick_params(which='minor', length=self.params.TickStyleDic["MinorLength"],
                                          width=self.params.TickStyleDic["MinorWidth"],
                                          left=tick_l, right=tick_r, color=tcolor, direction="in")
        lw = self.params.TickStyleDic["FrameLineWidth"]
        self.ax.spines["top"].set_linewidth(lw)
        self.ax.spines["right"].set_linewidth(lw)
        self.ax.spines["bottom"].set_linewidth(lw)
        self.ax.spines["left"].set_linewidth(lw)

        # ウィンドウサイズ指定の反映
        if self.isDarkMode:
            pass
        else:
            _canvas_size = self.canvas.size()
            _menubar_h = 0.0
            _toolbar_h = 0.0
            _statusbar_h = 0.0
            if not self.isDarkMode:
                _menubar_h = self.canvas.window().menubar.size().height()
                _toolbar_h = self.canvas.window().toolbar.size().height()
                _statusbar_h = self.canvas.window().statusbar.size().height()
            _bars_h = _menubar_h + _toolbar_h + _statusbar_h
            _win_size = self.canvas.window().size()
            _pad_h = _win_size.height() - _bars_h - _canvas_size.height()
            _pad_w = _win_size.width() - _canvas_size.width()
            if sliceMode:
                target = self.DrawDic["MultiMode"]
            else:
                target = self.DrawDic["SingleMode"]
            _new_size = [None, None]
            if self.params.window_size[0] is None:
                _new_size[0] = _win_size.width()
                # self.params.window_size[0]=_win_size.width()-_pad_w #[inamura 181112]
                self.params.window_size[0] = self.canvas.size(
                ).width() * target["MainPosi"][2]
            else:
                # _new_size[0] = _pad_w + self.params.window_size[0]
                _new_size[0] = _pad_w + \
                    self.params.window_size[0] / target["MainPosi"][2]
            if self.params.window_size[1] is None:
                _new_size[1] = _win_size.height()
                # self.params.window_size[1]=_win_size.height()-(_pad_h+_bars_h) #[inamura 181112]
                self.params.window_size[1] = self.canvas.size(
                ).height() * target["MainPosi"][3]
            else:
                # _new_size[1] = _pad_h + _bars_h + self.params.window_size[1]
                _new_size[1] = _pad_h + _bars_h + \
                    self.params.window_size[1] / target["MainPosi"][3]
            dpi = matplotlib.rcParams["figure.dpi"]
            w, h = self.CalcFigSizeFromDrawSize(
                self.params.window_size[0], self.params.window_size[1], dpi)
            _new_size[0] = int(w * dpi) + _pad_w
            _new_size[1] = int(h * dpi + _bars_h + _pad_h)

            logging.debug("D2Chart::PlotD2Chart new_size {0}".format(_new_size))
            self.canvas.window().resize(*_new_size)

        # figure size
        w = self.fig.get_figwidth()
        h = self.fig.get_figheight()

        bbox_main, bbox_right, bbox_top = self.GetBBox()
        _prop_title = prop.copy()
        _prop_title.set_size(self.params.fontDict['title'][0])
        _prop_title.set_name(self.params.fontDict['title'][1])
        _prop_comment = prop.copy()
        _prop_comment.set_size(self.params.fontDict['comment'][0])
        _prop_comment.set_name(self.params.fontDict['comment'][1])
        if sliceMode:  # スライス軸大きさ維持
            xy_top = bbox_top.get_points()
            xy_right = bbox_right.get_points()
            _title_text = self.fig.text(xy_top[0][0] - 0.3 / w, xy_top[1][1] + 0.3 / h, title,
                                        verticalalignment="top", horizontalalignment="left",
                                        fontproperties=_prop_title)
            _title_text.set_gid("title_text")
            comment = comment.replace(";", "")
            try:
                _comment_text = self.fig.text(xy_right[0][0], xy_top[1][1], comment,
                                              verticalalignment="top", horizontalalignment="left",
                                              fontproperties=_prop_comment, wrap=True)
            except AttributeError:
                _comment_text = self.fig.text(xy_right[0][0], xy_top[1][1], comment,
                                              verticalalignment="top", horizontalalignment="left",
                                              fontproperties=_prop_comment)
            _comment_text.set_gid("comment_text")
        else:
            xy_main = bbox_main.get_points()
            xy_right = bbox_right.get_points()
            _title_text = self.fig.text(xy_main[0][0] - 0.3 / w, 0.85 + 0.15 * xy_main[1][1], title,
                                        verticalalignment="top", horizontalalignment="left",
                                        fontproperties=_prop_title)
            _title_text.set_gid("title_text")
            """
            try:
                _comment_text = self.fig.text(xy_main[0][0]-0.3/w, 0.7+0.3*xy_main[1][1], comment,
                                        verticalalignment = "top", horizontalalignment = "left",
                                        fontproperties=prop, wrap=True)
            except AttributeError:
                _comment_text = self.fig.text(xy_main[0][0]-0.3/w, 0.7+0.3*xy_main[1][1], comment,
                                        verticalalignment = "top", horizontalalignment = "left",
                                        fontproperties=prop)
            _comment_text.set_gid("comment_text")
            return
            """
            colms = comment.split(";")
            colms_width = (xy_main[1][0] - xy_main[0][0]) / float(len(colms))
            for i in range(len(colms)):
                x0 = xy_main[0][0] - 0.3 / w + colms_width * i
                y1 = 0.7 + 0.3 * xy_main[1][1]
                try:
                    _comment_text = self.fig.text(x0, y1, colms[i],
                                                  verticalalignment="top", horizontalalignment="left",
                                                  fontproperties=_prop_comment, wrap=True)
                    _comment_text.set_gid("comment_text%d" % i)
                except AttributeError:
                    _comment_text = self.fig.text(x0, y1, colms[i],
                                                  verticalalignment="top", horizontalalignment="left",
                                                  fontproperties=_prop_comment)
                    _comment_text.set_gid("comment_text%d" % i)

        self.ax.callbacks.connect('xlim_changed', self.parent.OnXYlimChanged)
        self.ax.callbacks.connect('ylim_changed', self.parent.OnXYlimChanged)
        return

    ###################################################
    def _MakeXScale(self, scalePrm, x_range, prop):
        """
        X軸作成 (転置の場合はYスケールデータより作成)
        @param scalePrm   スケールパラメータ
        @param x_range   データの範囲
        @param prop      フォントのプロパティ
        @retval 無し
        """
        # 範囲が自動か
        if scalePrm.autoRange:
            # データの範囲を設定
            self.ax.set_xlim(x_range[0], x_range[1])
        else:
            # 指定された範囲を設定
            # 範囲の初期設定として最大値と最小値
            xmin = x_range[0]
            xmax = x_range[1]
            # 与えられた値がNoneでなければその値で置き換え
            if scalePrm.range[0] is not None:
                xmin = scalePrm.range[0]
            if scalePrm.range[1] is not None:
                xmax = scalePrm.range[1]
            self.ax.set_xlim(xmin, xmax)

        # ログ表示
        if scalePrm.logScale:
            self.ax.set_xscale("log")
            # self.ax.set_xscale("log", nonposx="clip")
            # self.ax.set_xscale("symlog")
        else:
            self.ax.set_xscale("linear")

        # Font設定の転置対応
        if self.params.transPosition:
            _ticks_font_settings = self.params.fontDict['yticks']
            _label_font_settings = self.params.fontDict['y']
        else:
            _ticks_font_settings = self.params.fontDict['xticks']
            _label_font_settings = self.params.fontDict['x']
        # チックマークが自動でないならば
        if not scalePrm.autoTick:
            # チックマークフォーマットを設定
            self.ax.xaxis.set_major_formatter(
                StrMethodFormatter(self._MakeTickLabels(scalePrm)))

        # チックマークラベルのフォントサイズを設定
        setp(self.ax.get_xticklabels(),
             size=_ticks_font_settings[0], fontname=_ticks_font_settings[1])

        # ラベルを表示
        # self.ax.set_xlabel(scalePrm.label, fontsize = 9,fontproperties=prop)
        if scalePrm.unit != "":
            label_unit = "{} ({})".format(scalePrm.label, scalePrm.unit)
        else:
            label_unit = scalePrm.label
        self.ax.set_xlabel(label_unit, size=_label_font_settings[0], fontname=_label_font_settings[1])

        # 軸反転
        if scalePrm.inversion:
            self.ax.invert_xaxis()

    ###################################################
    def _MakeYScale(self, scalePrm, y_range, prop):
        """
        Y軸作成 (転置の場合はXスケールデータより作成)
        @param scalePrm   スケールパラメータ
        @param y_range   データの範囲
        @param prop      フォントのプロパティ
        @retval 無し
        """
        # 範囲が自動か
        if scalePrm.autoRange:
            # データの範囲を設定
            self.ax.set_ylim(y_range[0], y_range[1])
        else:
            # 指定された範囲を設定
            # 範囲の初期設定として最大値と最小値
            ymin = y_range[0]
            ymax = y_range[1]
            # 与えられた値がNoneでなければその値で置き換え
            if scalePrm.range[0] is not None:
                ymin = scalePrm.range[0]
            if scalePrm.range[1] is not None:
                ymax = scalePrm.range[1]
            self.ax.set_ylim(ymin, ymax)

        # ログ表示
        if scalePrm.logScale:
            self.ax.set_yscale("log")
            # self.ax.set_yscale("log", nonposy="clip")
            # self.ax.set_yscale("symlog")
        else:
            self.ax.set_yscale("linear")

        # Font設定の転置対応
        if self.params.transPosition:
            _ticks_font_settings = self.params.fontDict['xticks']
            _label_font_settings = self.params.fontDict['x']
        else:
            _ticks_font_settings = self.params.fontDict['yticks']
            _label_font_settings = self.params.fontDict['y']
        # チックマークが自動でないならば
        if not scalePrm.autoTick:
            # チックマークフォーマットを設定
            self.ax.yaxis.set_major_formatter(
                StrMethodFormatter(self._MakeTickLabels(scalePrm)))

        # チックマークラベルのフォントサイズを設定
        setp(self.ax.get_yticklabels(),
             size=_ticks_font_settings[0], fontname=_ticks_font_settings[1])

        # ラベルを表示
        self.ax.yaxis.tick_left()
        # self.ax.set_ylabel(scalePrm.label, fontsize = 9, fontproperties=prop)
        if scalePrm.unit != "":
            label_unit = "{} ({})".format(scalePrm.label, scalePrm.unit)
        else:
            label_unit = scalePrm.label
        self.ax.set_ylabel(label_unit, size=_label_font_settings[0], fontname=_label_font_settings[1])

        # 軸反転
        if scalePrm.inversion:
            self.ax.invert_yaxis()

    ###################################################
    def _MakeTickLabels(self, scalePrm):
        """
        チックラベルフォーマット
        @param scalePrm   スケールパラメータ
        @retval チックラベルフォーマット
        """
        # チックマークラベルのフォーマットを作成
        if scalePrm.format == 0:
            sformat = "{{x:.{0}f}}".format(scalePrm.precision)
        else:
            sformat = "{{x:.{0}e}}".format(scalePrm.precision)
        return sformat

    ###################################################
    def OnNotifyChangePrms(self, wid, evt, value=None):
        """
        パラメータ変更メッセージ処理
        @param wid   ウィンドウID
        @param  evt  イベント
        @retval 無し
        """
        # パラメータをメイン画面より取得
        self.params = self.ifi.GetProperty('params')
        # マップを描画
        self.PlotD2Chart()
        # 領域を描画
        self.DrawRegions()
        # グラフを再描画
        self.canvas.draw()

    ###################################################
    def ChangeData(self, data):
        """
        データ変更処理
        @param  data (tpl(array, array,array) ) 表示データ
        @retval 無し
        """
        # データの初期化
        self.InitData(data)
        # マップを描画
        self.PlotD2Chart()
        # 領域を描画
        self.DrawRegions()
        # グラフを再描画
        self.canvas.draw()

    ###################################################
    def OnNotifyRedrawRegions(self, wid, evt, regInfos):
        """
        領域変更メッセージ処理
        @param wid   ウィンドウID
        @param  evt  イベント
        @param  regInfos  領域情報のリスト　[領域名、カラー、値のリスト、選択否選択のbool]
        @retval 無し
        """
        self.regInfos = regInfos
        # 領域を描画
        self.DrawRegions()
        # グラフを再描画
        self.canvas.draw()

    ###################################################
    def DrawRegions(self):
        """
        領域描画
        @param  無し
        @retval 無し
        """
        # 選択色を取得
        ec_color = CURSOL_COLORS[self.params.colorMap]
        # 領域をクリア
        self.regAxes.clear()
        # 各領域の取り出すを取り出す
        for region in self.regInfos:
            rname, color, valueOrgs, isSelected = region
            # 転置で元の情報が変更されないようにコピーしておく
            values = valueOrgs[:]
            # x軸とY軸の転置が指定されていたなら
            if self.params.transPosition:
                self.TransPosition(rname, values)
            # 幅無しのラインであれば
            if rname == "Line" and (values[4] in [None, ""]):
                if isSelected:
                    line = lines.Line2D([values[0], values[2]], [
                                        values[1], values[3]], color=ec_color, lw=1.5)
                else:
                    line = lines.Line2D([values[0], values[2]], [
                                        values[1], values[3]], color=color, lw=1.5, alpha=0.4)
                self.regAxes.add_line(line)
            else:
                if rname == "Point":
                    xx, yy = valueOrgs[:2]
                    # 当該位置のピクセル情報を取得
                    pixel_info = self.GetPixel(xx, yy)
                    # データ範囲外か
                    if pixel_info is None:
                        continue
                    # x軸とY軸の転置が指定されていたなら
                    if self.params.transPosition:
                        xx = pixel_info[0]
                        xdif = pixel_info[2]
                        pixel_info[0] = pixel_info[1]
                        pixel_info[1] = xx
                        pixel_info[2] = pixel_info[3]
                        pixel_info[3] = xdif
                    # ピクセルサイズの矩形を表示
                    if isSelected:
                        diagram = Rectangle((pixel_info[0], pixel_info[1]), pixel_info[2], pixel_info[3], fc=color,
                                            ec=ec_color, lw=1.5, alpha=0.4, gid="selected")
                    else:
                        diagram = Rectangle((pixel_info[0], pixel_info[1]), pixel_info[2], pixel_info[3], fc=color,
                                            ec='none', lw=0.1, alpha=0.4)
                else:
                    lw = 0.1
                    if isSelected:
                        lw = 1.5
                    diagram = self.MakeDiagram(
                        rname, values, 'none', color, isSelected, 0.4, lw)
                    if isSelected:
                        # diagram.set_gid("selected")
                        if diagram is not None:
                            diagram.set_gid("selected")
                # self.regAxes.add_patch(diagram)
                if diagram is not None:
                    self.regAxes.add_patch(diagram)

        # スケールをカラーマップのスケールに合わせる
        self.regAxes.set_xlim(self.ax.get_xlim())
        self.regAxes.set_ylim(self.ax.get_ylim())
        # スケールのラベルを消す
        self.regAxes.set_xticks([])
        self.regAxes.set_yticks([])

    ###################################################
    def OnNotifyDrawSliceRegion(self, eventSrc, evt, data):
        """
        スライス領域描画イベントハンドラ
        Parameters:
            eventSrc イベント発生元
            evt イベントデータ
            data (x, y) or (x0, y0, x1, y1)
        """
        # 選択色を取得
        ec_color = CURSOL_COLORS[self.params.colorMap]
        # スライスをクリア
        self.sliceAxes.clear()

        # 領域を再描画
        if self.ifi.GetProperty("reg_show_mode"):
            self.DrawRegions()

        if len(data) == 2:
            line = lines.Line2D(self.ax.get_xlim(), [
                                data[1], data[1]], color=ec_color, lw=1.5, alpha=0.4)
            self.sliceAxes.add_line(line)
            line = lines.Line2D(
                [data[0], data[0]], self.ax.get_ylim(), color=ec_color, lw=1.5, alpha=0.4)
            self.sliceAxes.add_line(line)
        else:
            xlim = self.ax.get_xlim()
            ylim = self.ax.get_ylim()
            diagram = self.MakeDiagram(
                "Rectangle", (xlim[0], data[1], xlim[1], data[3]), 'none', 'w', True, 0.4, 0.1)
            self.sliceAxes.add_patch(diagram)
            diagram = self.MakeDiagram(
                "Rectangle", (data[0], ylim[0], data[2], ylim[1]), 'none', 'w', True, 0.4, 0.1)
            self.sliceAxes.add_patch(diagram)
            line = lines.Line2D(
                xlim, [data[1], data[1]], color=ec_color, lw=1.5, alpha=0.4)
            self.sliceAxes.add_line(line)
            line = lines.Line2D(
                xlim, [data[3], data[3]], color=ec_color, lw=1.5, alpha=0.4)
            self.sliceAxes.add_line(line)
            line = lines.Line2D([data[0], data[0]], ylim,
                                color=ec_color, lw=1.5, alpha=0.4)
            self.sliceAxes.add_line(line)
            line = lines.Line2D([data[2], data[2]], ylim,
                                color=ec_color, lw=1.5, alpha=0.4)
            self.sliceAxes.add_line(line)

        # スケールをカラーマップのスケールに合わせる
        self.sliceAxes.set_xlim(self.ax.get_xlim())
        self.sliceAxes.set_ylim(self.ax.get_ylim())
        # スケールのラベルを消す
        self.sliceAxes.set_xticks([])
        self.sliceAxes.set_yticks([])

        # self.canvas.draw() # 不要？

    #########################################
    def TransPosition(self, rname, values):
        """
        X軸とY軸のデータ倒置
        @param  rname    領域名
        @param  values   数値情報のリスト
        @retval 無し
        """
        # X値とY値の入れ替え
        yy0 = values[1]
        values[1] = values[0]
        values[0] = yy0
        if rname == "Line" or rname == "Rectangle":
            yy1 = values[3]
            values[3] = values[2]
            values[2] = yy1
        elif rname == "Sector":
            # 反転
            values[3] = self._Rotate(values[2], values[3])
        elif rname == "Ring Sector":
            # 反転
            values[4] = self._Rotate(values[2], values[4])

    #########################################
    def _Rotate(self, radius, deg1):
        """
        反転
        @param   radius  半径
        @param   deg1    回転前の角度
        @retval 反転後の角度
        """
        if deg1 == 0.0 or deg1 == 180.0:
            deg1 += 90.0
        else:
            rd0 = deg1 * math.pi / 180.0
            x0 = radius * math.cos(rd0)
            y0 = radius * math.sin(rd0)
            rd0 = math.atan(x0 / y0)
            deg1 = rd0 * 180.0 / math.pi
            if y0 < 0.0:
                deg1 += 180.0
        if deg1 < 0:
            deg1 += 360.0
        return deg1

    #########################################
    def MakeDiagram(self, name, values, ec, fc, cflag, alpha, lw):
        """
        描画図形の作成
        @param   name    領域名
        @param   values  領域データ
        @param   ec      境界線の色
        @param   fc      塗りつぶし色
        @param   cflag   カレント領域
        @param   alpha   半透明
        @param   lw      境界線のライン幅
        @retval 図形
        """
        if cflag:
            ec = CURSOL_COLORS[self.params.colorMap]
        # 幅付きのラインであれば
        if name == "Line":
            x0, y0, x1, y1, width = values[:5]
            w2 = width / 2.0
            # 水平か
            if y1 == y0:
                xdef = x1 - x0
                ydef = width
                diagram = Rectangle((x0, y0 - w2), xdef,
                                    ydef, fc=fc, ec=ec, alpha=alpha, lw=lw)
            # 垂直か
            elif x1 == x0:
                xdef = width
                ydef = y1 - y0
                diagram = Rectangle((x0 - w2, y0), xdef,
                                    ydef, fc=fc, ec=ec, alpha=alpha, lw=lw)
            # 斜め
            else:
                # 矩形の4隅の座標を計算
                theta = math.atan((x1 - x0) / (y1 - y0))
                xdef = w2 * math.cos(theta)
                ydef = w2 * math.sin(theta)
                xx0 = x0 - xdef
                xx1 = x0 + xdef
                yy0 = y0 + ydef
                yy1 = y0 - ydef
                xx2 = x1 + xdef
                xx3 = x1 - xdef
                yy2 = y1 - ydef
                yy3 = y1 + ydef

                # ポリゴンで矩形を作成
                diagram = Polygon([[xx0, yy0], [xx1, yy1], [xx2, yy2], [xx3, yy3]], closed=True, fill=True, fc=fc,
                                  ec=ec, alpha=alpha, lw=lw)
        # 矩形
        elif name == "Rectangle":
            if (values[2] is None) or (values[3] is None):
                return None
            xdef = values[2] - values[0]
            ydef = values[3] - values[1]
            diagram = Rectangle(
                (values[0], values[1]), xdef, ydef, fc=fc, ec=ec, alpha=alpha, lw=lw)
        # 円
        elif name == "Circle":
            if values[3] is None:
                return None
            diagram = Circle((values[0], values[1]),
                             values[2], fc=fc, ec=ec, alpha=alpha, lw=lw)
        # リング
        elif name == "Ring":
            x0, y0, radius, rwidth = values[:4]
            if (radius is None) or (rwidth is None):
                return None
            # 外円の半径
            radius += rwidth / 2.0
            diagram = Wedge((x0, y0), radius, 0, 360, width=(rwidth * 2.0), fc=fc, ec=ec, alpha=alpha, lw=lw)
        # 扇形
        elif name == "Sector":
            start = values[4] - values[5]
            end = values[4] + values[5]
            if end > 360.0:
                end -= 360.0
            diagram = Wedge((values[0], values[1]), values[2],
                            start, end, fc=fc, ec=ec, alpha=alpha, lw=lw)
        # リング扇形
        elif name == "Ring Sector":
            x0, y0, radius, rwidth, angle1, angle2 = values
            start = angle1 - angle2
            end = angle1 + angle2
            if end > 360.0:
                end -= 360.0
            # 外円の半径
            radius += rwidth / 2.0
            diagram = Wedge((x0, y0), radius, start, end, width=(rwidth * 2.0), fc=fc, ec=ec, alpha=alpha, lw=lw)

        return diagram

    #########################################
    def ShowDragCursol(self, name, values):
        """
        ドラッグカーソル表示
        @param   name    領域名称
        @param   values  領域データ
        @retval 無し
        """
        # 領域を表示
        self.DrawRegions()

        # カーソルカラーを取得
        cs_color = CURSOL_COLORS[self.params.colorMap]

        if name == "Line":
            line = lines.Line2D([values[0], values[2]], [
                                values[1], values[3]], color=cs_color, lw=2.0)
            self.regAxes.add_line(line)
        else:
            diagram = self.MakeDiagram(
                name, values, cs_color, 'none', False, 1.0, 2.0)
            self.regAxes.add_patch(diagram)
        # カーソルサブプロットの座標を不可視にする
        setp(self.regAxes.get_xticklabels(), visible=False)
        setp(self.regAxes.get_yticklabels(), visible=False)

        # グラフを再表示
        self.canvas.draw()

    #########################################
    def GetIntValue(self, x, y):
        """
        マウス位置の強度データを返す
        @param   x  マウス位置のXデータ
        @param   y  マウス位置のY データ
        @retval 強度データ、マスク値の場合はNone
        """
        if self.params.transPosition:
            xx = x
            x = y
            y = xx
        indices = self.GetIndex(x, y)
        if indices is None:
            return None

        xi, yi = indices
        intV = self.Z[yi][xi]

        if intV == MASKVALUE:
            return None
        return intV

    #########################################
    def GetPixel(self, x, y):
        """
        指定座標のピクセル(矩形)情報を返す
        @param  x (float) Xデータ
        @param  y (float) Yデータ
        @retval (x0,y0, xdiff,ydiff) ピクセル情報（座標、幅）
        """
        print("{0}::{1}".format(self.__class__.__name__,
                                sys._getframe().f_code.co_name))
        indices = self.GetIndex(x, y)
        if indices is None:
            return None

        xi, yi = indices
        x0 = self.X[0][xi]
        xdiff = self.X[0][xi + 1] - self.X[0][xi]
        y0 = self.Y[yi][0]
        ydiff = self.Y[yi + 1][0] - self.Y[yi][0]
        return [x0, y0, xdiff, ydiff]

    #########################################
    def GetIndex(self, x, y):
        """
        指定座標のインデックスを返す
        @param  x (float) Xデータ
        @param  y (float) Yデータ
        @retval (xindex,yindex)
        """
        ycount = len(self.Z)
        xcount = len(self.Z[0])
        xi = xcount - 1
        yi = ycount - 1

        # 範囲外チェック
        if x < self.X[0][0] or x > self.X[0][xcount]:
            return None
        if y < self.Y[0][0] or y > self.Y[ycount][0]:
            return None

        for i in range(xcount):
            if self.X[0][i] > x:
                xi = i - 1
                break

        for i in range(ycount):
            if self.Y[i][0] > y:
                yi = i - 1
                break

        return (xi, yi)

    #########################################
    def CalcFigSizeFromDrawSize(self, px, py, dpi=100.0):
        """
        Returns new fig size [inch] including sub-plot and colorbar calculated from given size [pixel] and plot parameters
        @param px (int) width [pixel]
        @param py (int) height [pixel]
        @param dpi (float) dot par inch
        @retval tuple of width and height [inch]
        """
        sliceMode = self.ifi.GetProperty("slice_mode")
        if sliceMode:
            target = self.DrawDic["MultiMode"]
        else:
            target = self.DrawDic["SingleMode"]
        # right axes width, top axes height
        right_width, top_height = target["SubSize"]

        # main axes
        main_width, main_height = target["MainSize"]

        # gap
        x_gap, y_gap = target["Gap"]

        # padding
        left_pad, bottom_pad, right_pad, top_pad = target["Padding"]

        # calculate
        if sliceMode:
            pix = 18  # Why needed 18 pixels for saved figure ?
            w = float(x_gap + right_width + right_pad) + \
                (float(px + pix) / float(dpi)) / (1.0 - target["MainPosi"][0])
            h = float(top_height + top_pad + y_gap) + \
                (float(py + pix) / float(dpi)) / (1.0 - target["MainPosi"][1])
        else:
            w = float(px) / float(dpi) / target["MainPosi"][2]
            h = float(py) / float(dpi) / target["MainPosi"][3]
        return (w, h)

    #########################################
    def GetBBox(self):
        """スライス軸の大きさを維持するBounding boxを返す

        Returns:
            list(BBox, BBox, BBox) main, right, top
        """
        sliceMode = self.ifi.GetProperty("slice_mode")
        if sliceMode:
            target = self.DrawDic["MultiMode"]
        else:
            target = self.DrawDic["SingleMode"]
        # right axes width, top axes height
        right_width, top_height = target["SubSize"]

        # main axes
        main_width, main_height = target["MainSize"]

        # gap
        x_gap, y_gap = target["Gap"]

        # padding
        left_pad, bottom_pad, right_pad, top_pad = target["Padding"]

        # figure size
        w = self.fig.get_figwidth()
        h = self.fig.get_figheight()

        # get current bboxes
        bbox_main = self.ax.get_position()
        xy_main = bbox_main.get_points()
        if sliceMode:
            bbox_top = self.ax_top.get_position()
            xy_top = bbox_top.get_points()
            bbox_right = self.ax_right.get_position()
        else:
            bbox_top = None
            xy_top = None
            bbox_right = self.axc.get_position()
        xy_right = bbox_right.get_points()

        # calculate
        if w > (
                left_pad + main_width + x_gap + right_width + right_pad) and sliceMode:  # @ not slicemode : Use initial default value
            # xy_main[0][0] = left_pad/w
            xy_main[1][0] = 1.0 - (right_width + right_pad + x_gap) / w

            xy_right[1][0] = 1.0 - right_pad / w
            xy_right[0][0] = 1.0 - (right_width + right_pad) / w

            if sliceMode:
                xy_top[0][0] = xy_main[0][0]
                xy_top[1][0] = xy_main[1][0]
        else:
            pass
        if h > (
                bottom_pad + main_height + y_gap + top_height + top_pad) and sliceMode:  # @ not slicemode : Use initial default value
            xy_main[1][1] = 1.0 - (top_height + top_pad + y_gap) / h
            xy_right[1][1] = xy_main[1][1]
            if sliceMode:
                xy_top[1][1] = 1.0 - top_pad / h
                xy_top[0][1] = 1.0 - (top_height + top_pad) / h
        else:
            pass

        # update
        bbox_main.update_from_data_xy(xy_main)
        bbox_right.update_from_data_xy(xy_right)
        if sliceMode:
            bbox_top.update_from_data_xy(xy_top)

        return bbox_main, bbox_right, bbox_top

    #########################################
    def GetQuadMesh(self):
        """
        描画された2次元プロット情報を返す
        """
        meshs = self.ax.findobj(match=QuadMesh)
        return meshs[0]

    #########################################
    def OnNotifyOverPlot(self, wid, evtType, traces):
        """
        オーバープロットデータ変更イベント処理
        @param  wid イベント発生元のインスタンス
        @param  evtType イベントタイプ
        @param  traces トレースオブジェクト
        @retval 無し
        """
        # トレースデータ表示
        self.traces = traces
        self.DrawOverPlot()

    #########################################
    def DrawOverPlot(self):
        """
        オーバプロットトレースの描画
        @param  無し
        @retval 無し
        """
        if self.traces is None:
            return
        # 先の描画をクリア
        self.ovplot.clear()

        for trace in self.traces:
            if trace.draw:
                if trace.xlabel == self.params.yscale.label:
                    if trace.edata is None or (not trace.errorbar):
                        self.ovplot.plot(trace.ydata, trace.xdata, linewidth=trace.linewidth,
                                         color=trace.color, linestyle=trace.linestyle,
                                         marker=trace.marker, markersize=trace.markersize,
                                         mfc=trace.markerface, mec=trace.color, mew=trace.linewidth)
                    else:
                        self.ovplot.errorbar(trace.ydata, trace.xdata, yerr=trace.edata, linewidth=trace.linewidth,
                                             color=trace.color, linestyle=trace.linestyle,
                                             capsize=int(trace.markersize / 2),
                                             marker=trace.marker, markersize=trace.markersize,
                                             mfc=trace.markerface, mec=trace.color, mew=trace.linewidth)
                    # 描画範囲を合わせる
                    self.ovplot.set_ylim(self.ax.get_ylim())
                    self.ovplot.set_xlim(self.ax.get_xlim())
                    # if trace.ylabel != self.zlabel:
                    #    self.ovplot.set_xlim(self.ax.get_xlim())
                else:
                    if trace.edata is None or (not trace.errorbar):
                        self.ovplot.plot(trace.xdata, trace.ydata, linewidth=trace.linewidth,
                                         color=trace.color, linestyle=trace.linestyle,
                                         marker=trace.marker, markersize=trace.markersize,
                                         mfc=trace.markerface, mec=trace.color, mew=trace.linewidth)
                    else:
                        self.ovplot.errorbar(trace.xdata, trace.ydata, yerr=trace.edata, linewidth=trace.linewidth,
                                             color=trace.color, linestyle=trace.linestyle,
                                             capsize=int(trace.markersize / 2),
                                             marker=trace.marker, markersize=trace.markersize,
                                             mfc=trace.markerface, mec=trace.color, mew=trace.linewidth)

                    # 描画範囲を合わせる
                    self.ovplot.set_xlim(self.ax.get_xlim())
                    self.ovplot.set_ylim(self.ax.get_ylim())
                    # if trace.ylabel != self.zlabel:
                    #    self.ovplot.set_ylim(self.ax.get_ylim())
        # スケール文字を消す
        self.ovplot.set_xticks([])
        self.ovplot.set_yticks([])
        self.canvas.draw()

    #########################################
    def on_draw(self, event):
        """スライス軸の大きさを維持設定

        """
        logging.debug("D2Chart::on_draw")
        sliceMode = self.ifi.GetProperty("slice_mode")
        if sliceMode:
            target = self.DrawDic["MultiMode"]
        else:
            target = self.DrawDic["SingleMode"]

        bbox_main, bbox_right, bbox_top = self.GetBBox()
        if sliceMode:
            xy_main = None
            xy_top = bbox_top.get_points()
        else:
            xy_main = bbox_main.get_points()
            xy_top = None
        xy_right = bbox_right.get_points()

        # update
        self.ax.set_position(bbox_main)
        self.regAxes.set_position(bbox_main)
        self.sliceAxes.set_position(bbox_main)
        if sliceMode:
            self.ax_right.set_position(bbox_right)
            self.ax_top.set_position(bbox_top)
        else:
            self.axc.set_position(bbox_right)

        # figure size
        w = self.fig.get_figwidth()
        h = self.fig.get_figheight()

        # update text position
        self.fig.get_children()
        for child in self.fig.get_children():
            if child.get_gid() == "title_text":
                if sliceMode:
                    child.set_position(
                        (xy_top[0][0] - 0.3 / w, xy_top[1][1] + 0.3 / h))
                else:
                    child.set_position(
                        (xy_main[0][0] - 0.3 / w, 0.85 + 0.15 * xy_main[1][1]))
            elif child.get_gid() == "comment_text":
                if sliceMode:
                    child.set_position((xy_right[0][0], xy_top[1][1]))
                else:
                    child.set_position(
                        (xy_main[0][0] - 0.3 / w, 0.7 + 0.3 * xy_main[1][1]))

        # draw_eventがループしないようにいったんdisconnectしてからdraw
        self.canvas.mpl_disconnect(self.cid_draw)
        self.canvas.draw()
        self.cid_draw = self.canvas.mpl_connect('draw_event', self.on_draw)

        return False

#######################################
#  DataConverter
#######################################


class DataConverter(object):
    """uGao 汎用2次元プロッタ 用のデータ変換クラス

    注意：強度合計計算時の誤差は用いないこと。
    EC.Sum()利用時にYの変更に合わせてEを変更することができないため。

    Attributes:
        indices_sort (list): np.argsortにより得たindexリスト
    """

    #########################################
    def __init__(self):
        """コンストラクタ
        """
        self.xkey = None
        self.ykey = None
        self.intKey = None
        self.rkey = None
        self.intRange = None
        self.indices_sort = None
        self.slicer = None
        self._eca = None  # ECMスライス結果保持用
        self.params = {"bank_separators": []}

        self.isInelasticPowder = False  # Whether data is powder type or not
        self.arraySlicer = None        # MlfArraySlicer instance

    #########################################
    def PutSlicedEca(self):
        return self._eca

    #########################################
    def ConvertData(self, data, keys, intRange, isdetectmapmode=True):
        """データ設定処理

        Parameters:
            data (ECM, ECA, list): 表示データ
            keys (str, str, str, [str]) : 軸指定キー
            intRange (float, float): 合計範囲

        Returns:
            numpy array tuple (Int, X,Y): 表示用に変換したデータ
        """
        # self.xbin = None [20170424, TI] TODO: Obsolete?
        # self.ybin = None [20170424, TI] TODO: Obsolete?
        self.xkey = keys[0]  # X軸キー
        self.ykey = keys[1]  # y軸キー
        self.intKey = keys[2]  # 強度軸キー
        self.rkey = None  # 合計範囲指定キー
        if len(keys) == 4:
            self.rkey = keys[3]
        self.intRange = intRange  # 合計範囲
        withBankGap = isdetectmapmode

        self._eca = None  # 再初期化。_ConvertECAが呼び出された場合はNoneのまま
        self.isInelasticPowder = False
        if isinstance(data, tuple) or isinstance(data, list):
            if (len(data) < 3):
                raise PlotException('Common', 'C040', ())
            if not isinstance(data[0], ndarray) \
                    or not isinstance(data[1], ndarray) or not isinstance(data[2], ndarray):
                raise PlotException('Common', 'C040', ())
            logging.debug("This is numpy array data.")
            # 2次元データか
            if data[0].ndim != 2:
                raise PlotException('UGAO', 'U003', ("2D array",))

            intY = data[0]
            numX = intY.shape[0] + 1
            numY = intY.shape[1] + 1
            # エラー付きのデータか
            if (len(data) > 3):
                XX = data[2]
                YY = data[3]
            else:
                XX = data[1]
                YY = data[2]

            if XX.ndim != 2 or YY.ndim != 2:
                raise PlotException('UGAO', 'U003', ("2D array",))

            if XX.shape[0] != numX or XX.shape[1] != numY:
                raise PlotException('UGAO', 'U004', ())
            if YY.shape[0] != numX or YY.shape[1] != numY:
                raise PlotException('UGAO', 'U004', ())
            self.indices_sort = None  # [20170424, TI] TODO: 未考慮。落ち着いたら考える。
            ndata = [intY, XX, YY]
        elif self.CheckInelasticPowderData(data):
            ndata = self._ConvertInelasticPowderData(data)
            if ndata is None:
                # raise PlotException('UGAO','',())
                raise UserWarining("Failed to convert to powder data")
            self.isInelasticPowder = True
        elif isinstance(data, Manyo.ElementContainerMatrix):
            logging.debug("This is ElementContainerMatrix data.")
            # Matrix を numpy array データに変換
            ndata = self._ConvertECM(data, withBankGap)
        elif isinstance(data, Manyo.ElementContainerArray):
            logging.debug("This is ElementContainerArray data.")
            # Array を numpy array データに変換
            ndata = self._ConvertECA(data)

        else:
            raise PlotException('Common', 'C040', ())

        # Preparing MlfArraySlicer
        if self.arraySlicer is not None:
            del self.arraySlicer
        if self.isInelasticPowder:  # data must be ECM for inelastic powder
            if isinstance(data, Manyo.ElementContainerArray):
                ecm = Manyo.ElementContainerMatrix(data.PutHeader())
                ecm.Add(data)
                self.arraySlicer = mm.MlfArraySlicer(ecm)
            else:
                self.arraySlicer = mm.MlfArraySlicer(data)
        elif self._eca is not None:      # self._eca is ECA sliced from ECM
            self.arraySlicer = mm.MlfArraySlicer(self._eca)
        else:                      # data is ECA
            self.arraySlicer = mm.MlfArraySlicer(data)

        # Return list of numpy arrays
        return ndata
    #######################################

    def _ConvOldM2PlotKeys(self, data, isEcm):
        """
        M2Plotで使用できたヘッダ情報をM2PlotPlusで使用できるように変換する
        ECのヘッダに"XRANGE"があれば使用する
        @param data  (ElementContainerMatrix or ElementContainerArray)
        @param isEcm (bool) flag that data is ElementContainerMatrix or not
        @retval None
        """
        if isEcm:
            return
        else:
            hh = data.PutHeaderPointer()
            hh_ec0 = data(0).PutHeaderPointer()
            # ECのヘッダに"XRANGE"があればそれをXaxisとして扱う
            if hh_ec0.CheckKey("XRANGE") == 1:
                xtitle = "Xaxis"
                # ECAのヘッダに"XLABEL"があればそれをキーにする
                if hh.CheckKey("XLABEL") == 1:
                    xtitle = hh.PutString("XLABEL")
                # すでにキーとしてあれば、別名にする
                if hh_ec0.CheckKey(xtitle) == 1:
                    xtitle = "{}mpp".format(xtitle)

                # Xaxisを計算し新たにECのヘッダに加える
                for i in range(data.PutSize()):
                    ech = data(i).PutHeaderPointer()
                    xx = ech.PutDoubleVector("XRANGE")
                    # Header has no "Xaxis" key
                    if ech.CheckKey(xtitle) == 0:
                        ech.Add(xtitle, ((xx[0] + xx[1]) / 2.0))
                    else:
                        ech.OverWrite(xtitle, ((xx[0] + xx[1]) / 2.0))

    #########################################
    def _ConvertECM(self, ecm, withBankGap):
        """ECMデータ設定処理

        Parameters:
            ecm (ElementContainerMatrix): 変換前のデータ
            withBankGap (bool): withBankGap

        Returns:
            numpy array tuple (Int, X,Y): 表示用に変換したデータ
        """
        # データが空の場合のエラー処理
        numX = ecm.PutSize()  # ECAの数
        if numX == 0:
            logging.debug("Error!!  empty matrix.")
            raise PlotException('Common', 'C040', ())
        numY = ecm(0).PutSize()  # ECの数
        if numY == 0:
            logging.debug("Error!!  empty array in matrix.")
            raise PlotException('Common', 'C040', ())

        # 合計範囲指定キー適用前処理
        changeKey = False  # キー変更有無
        preXKey = ecm(0, 0).PutXKey()  # 元のXKey
        preYKey = ecm(0, 0).PutYKey()  # 元のYKey
        preEKey = ecm(0, 0).PutEKey()  # 元のEKey
        # 範囲指定キーが指定されていれば
        if preYKey != self.intKey:
            changeKey = True
        elif self.intRange is not None and (self.rkey is not None or self.rkey != preXKey):
            changeKey = True

        # キーの変更
        if changeKey:
            for i in range(numX):
                for j in range(numY):
                    ecm(i, j).SetKeys(self.rkey, self.intKey, preEKey)

        # スライス処理
        self.slicer = mm.MlfMatrixSlicer(ecm)
        self.slicer.SetSummationMode()
        self._eca = Manyo.ElementContainerArray()  # スライス結果代入用
        zrange = Manyo.MakeDoubleVector()  # 強度軸スライス範囲
        _xkey = ""
        if self.xkey is not None:
            _xkey = self.xkey
        _ykey = ""
        if self.ykey is not None:
            _ykey = self.ykey
        _rkey = ""
        if self.rkey is not None:
            _rkey = self.rkey
        if self.intRange is not None:
            zrange.resize(2)
            zrange[0] = float(self.intRange[0])
            zrange[1] = float(self.intRange[1])
        logging.debug("ECM slice params: {0}, {1}, {2}, {3}".format(
            _xkey, _ykey, _rkey, list(zrange)))
        self.slicer.GetPlaneAsDetectMap(self._eca, _xkey, _ykey, _rkey, zrange, withBankGap)

        # キーを戻す
        if changeKey:
            for i in range(numX):
                for j in range(numY):
                    ecm(i, j).SetKeys(preXKey, preYKey, preEKey)

        if _ykey == "":
            _ykey = "index"
        if self.intKey is None:
            _intKey = "Intensity"
        else:
            _intKey = self.intKey
        for i in range(self._eca.PutSize()):
            if _intKey != "Intensity":
                self._eca(i).RenameKey("Intensity", _intKey)

        h = self._eca.PutHeaderPointer()
        if h.PutKeyLocation("BANKSEPARATOR") == 4:
            # self.params["bank_separators"] = list(h.PutInt4Vector("BANKSEPARATOR"))
            if _xkey != "" and _xkey != "Index" and _xkey != "index":
                self.params["bank_separators"] = []
            else:  # If DetectMap mode ?
                bs_tmp = list(h.PutInt4Vector("BANKSEPARATOR"))
                bs_list = []
                num_of_sep = int(len(bs_tmp) / 2)
                for k in range(num_of_sep):
                    bs_list.append(bs_tmp[2 * k + 0] + k)
                    bs_list.append(bs_tmp[2 * k + 1] + k)
                self.params["bank_separators"] = bs_list

        return self._ConvertECA(self._eca)

    #########################################
    def _GetKeyValue(self, ecobj, key, keyType):
        """ECAのヘッダの指定された値を返す

        Parameters:
            ecobj (ECA or EC)
            key (string): キー
            keyType (int): 1: Int4, 2: Double

        Retunrs:
            float: 取得した値
        """
        h = ecobj.PutHeaderPointer()
        if keyType == 1:
            return float(h.PutInt4(key))
        return h.PutDouble(key)

    #########################################
    def _IfMasked(self, obj):
        """
        データ設定処理
        @param  obj (ElementContainer or Array) チェックするデータ
        @retval True: マスクされている
        """
        header = obj.PutHeaderPointer()
        if header.CheckKey("MASKED") == 1:
            masked = header.PutInt4("MASKED")
            if masked == 1:
                return True
        return False

    #########################################
    def _ConvertECA(self, eca):
        """ECAデータ設定処理

        Parameters:
            eca (ElementContainerArray): 変換前のデータ

        Returns:
            numpy array List (Int, X,Y): 表示用に変換したデータ
        """
        self._ConvOldM2PlotKeys(data=eca, isEcm=False)
        if self._IfMasked(eca):
            raise PlotException('Common', 'C040', ())
        # Array 中のECの数を取得
        numX = eca.PutSize()
        if numX == 0:
            logging.debug("Error!!  empty Array.")
            raise PlotException('Common', 'C040', ())
        # EC中のデータ数をチェック # 実際の数は後でもう一度確認する
        numZ = len(eca(0).PutList(self.intKey))
        if numZ == 0:
            logging.debug("Error!!  empty EC.")
            raise PlotException('Common', 'C040', ())

        xkeyType = 0  # 設定無きときはIndex
        # xkey のチェック
        if self.xkey is not None:
            xkeyType = self._GetKeyType(eca(0), self.xkey)

        xvalues = []  # X軸値リスト
        for i in range(numX):
            if self.xkey is not None:
                # ヘッダから指定されたKey の値を取得
                xvalues.append(self._GetKeyValue(eca(i), self.xkey, xkeyType))
            else:
                # インデックスを格納
                xvalues.append(float(i))

        # 昇順に整列
        self.indices_sort = np.argsort(xvalues).tolist()  # ソートするためのインデックスを取得
        xvalues.sort()

        # x bin 格納用のArray を準備
        h = np.zeros(numX + 1, np.float64)
        self._MakeBin(h, xvalues)

        yvalues = []  # y軸値リスト
        # ykey のチェック
        if self.ykey is not None:
            lsKeys = eca(0).PutKeysList()
            if not (self.ykey in lsKeys):
                raise PlotException(
                    'UGAO', 'U001', (self.ykey, "ElementContainer"))
            yvalues = eca(0).PutList(self.ykey)
        else:
            yvalues = eca(0).PutXList()
        numY = len(yvalues)

        zvalues = []  # 強度リスト（size確認用）
        # zkey のチェック
        if self.intKey is not None:
            lsKeys = eca(0).PutKeysList()
            if not (self.intKey in lsKeys):
                raise PlotException(
                    'UGAO', 'U001', (self.intKey, "ElementContainer"))
            zvalues = eca(0).PutList(self.intKey)
        else:
            zvalues = eca(0).PutYList()
        numZ = len(zvalues)

        # Xデータ数が強度より1個多ければ
        if (numZ + 1) == numY:
            # そのままArray に変換
            v = np.array(yvalues)
        # データ数が同一であればBin データを作る
        elif numZ == numY:
            v = np.zeros(numY + 1, np.float64)
            self._MakeBin(v, yvalues)

        # グリッド作成
        H, V = np.meshgrid(h, v)
        # 強度格納用の2次元Array を準備
        intY = np.zeros((numZ, numX), np.float64)
        for i in range(numX):
            for j in range(numZ):
                # マスク値にて初期化
                intY[j, i] = MASKVALUE

        for i in range(numX):
            # ECはマスクされているか
            if self._IfMasked(eca(i)):
                continue
            # インデックスか
            if self.xkey is None:
                index = i
            else:
                index = self.indices_sort[i]
            ec = eca(index)
            yy = ec.PutList(self.intKey)
            er = ec.PutEList()
            for j in range(numZ):
                # マスクデータでなければ
                if er[j] >= 0.0:
                    # 強度を設定
                    intY[j, i] = yy[j]

        # 描画用のデータを返す
        return (intY, H, V)

    #########################################
    def _MakeBin(self, array, values):
        """
        bin データ作成
        @param  array (ndarray) 結果を格納する Array
        @param  values (lst flat) 元の値
        @retval 無し
        """
        # データ数
        num = len(values)

        # データ数が1のときは特別処理
        if num == 1:
            if values[0] == 0.0:
                array[0] = -0.5
                array[1] = 0.5
            else:
                array[0] = -0.5 * values[0]
                array[1] = 0.5 * values[1]
            return
        # 最初の点
        array[0] = values[0] - (values[1] - values[0]) * 0.5
        for i in range(1, num):
            array[i] = (values[i] + values[i - 1]) * 0.5
        # 最後の点
        array[num] = values[num - 1] + (values[num - 1] - values[num - 2]) * 0.5

    #########################################
    def _GetKeyType(self, obj, key):
        """
        データ設定処理
        @param  obj (ECA or EC)
        @param  key (string) キー
        @retval (int) キーのタイプ 1: UInt4, 2: Double
        """
        # ヘッダーを取得
        header = obj.PutHeader()
        keyType = header.PutKeyLocation(key)

        # key が無い
        if keyType == 0:
            raise PlotException('UGAO', 'U001', (key, type(obj)))

        # key のタイプがInt または float では無い
        if keyType > 2:
            raise PlotException('UGAO', 'U002', (key, ))

        return keyType

    #########################################
    def PutLastSortIndices(self):
        return self.indices_sort

    #########################################
    def CheckInelasticPowderData(self, data):
        """
        データが非弾性散乱パウダーデータのフォーマットかどうかをチェック
        1. ElementContainerMatrixか、ElementContainerArrayであること
        2. ヘッダに入射エネルギー情報が含まれていること(key="Ei")
        3. 試料のタイプがPowderであること(key="SAMPLETYPE")
        @param data (ECM,ECA)
        @retval True : Inelastic powder data format
        @retval False: Not inelastic powder data format
        """
        # ElementContainerMatrix, ElementContainerArrayか
        if not isinstance(data, Manyo.ElementContainerMatrix) and not isinstance(data, Manyo.ElementContainerMatrix):
            return False

        # ヘッダーの取得
        mh = data.PutHeaderPointer()
        # 入射エネルギーが指定されているか
        if mh.CheckKey("Ei") == 0:
            return False

        # 試料のタイプがPowderに指定されているか
        if mh.CheckKey("SAMPLETYPE") == 1:
            smptype = mh.PutString("SAMPLETYPE").upper()
            if smptype.find("POWDER") < 0:
                return False
        else:
            return False

        return True

    #########################################
    def _ConvertInelasticPowderData(self, data):
        """
        非弾性散乱パウダーデータの変換処理
        @param data (ECM,ECA) 非弾性散乱パウダーデータ
        @retval None : 変換失敗
        @retval tupple of meshgrid array : 変換データ
        """

        # 非弾性散乱パウダーデータか
        if not self.CheckInelasticPowderData(data):
            return None

        # 入射エネルギーの取得
        Ei = data.PutHeaderPointer().PutDouble("Ei")
        if isinstance(data, Manyo.ElementContainerArray):
            eca = data
        elif isinstance(data, Manyo.ElementContainerMatrix):
            eca = data.Put(0)
        else:
            return None

        ebin = eca(0).PutXList()

        nEc = eca.PutSize()
        pols = []

        for i in range(nEc):
            ec = eca(i)
            ech = ec.PutHeader()
            if ((ech.CheckKey("MASKED") == 1) and (ech.PutInt4("MASKED") == 1)):
                continue
            pol = ech.PutDoubleVector("PixelPolarAngle")
            pols.append(pol[0] * math.pi / 180.0)

        if len(pols) < 2:
            return None

        pnum = len(pols)
        polr = pols[pnum - 1] - pols[pnum - 2] + pols[pnum - 1]
        pols.append(polr)
        hfGap = (pols[1] - pols[0]) / 2.0
        # 最初のPSD
        theta = pols[0] - hfGap
        # 1本のPSD の角度より1.5倍以上あるとき、バンクギャップとみなす
        gapTh = hfGap * 3.0

        thetas = [theta]
        gaps = []

        # PSD 両端部の散乱角を計算
        for i in range(pnum):
            # 確度の差分を求める
            gap = pols[i + 1] - pols[i]
            # バンクギャップでなければ
            if gap < gapTh:
                hfGap = gap / 2.0
                theta = pols[i] + hfGap
                thetas.append(theta)
                gaps.append(False)
            # バンクギャップ
            else:
                # 前の半値から
                theta = pols[i] + hfGap
                thetas.append(theta)
                gaps.append(False)
                theta = pols[i + 1] - hfGap
                thetas.append(theta)
                gaps.append(True)
            # 角度差は常に一定ではないのでその都度ギャップ用閾値を作成する
            gapTh = hfGap * 3.0

        enL = []
        datL = []
        erL = []
        # バンクギャップ用のマスク値作成
        mskYY = []
        mskEr = []
        for i in range(len(ebin) - 1):
            mskYY.append(MASKVALUE)
            mskEr.append(0.0)

        k = 0
        # ギャップの数に応じてデータを作成する
        for j in range(len(gaps)):
            # バンクギャップか
            if gaps[j]:
                datL.append(mskYY)
                erL.append(mskEr)
            else:
                ec = eca.PutPointer(k)
                yy = ec.PutYList()
                er = ec.PutEList()

                datL.append(yy)
                erL.append(er)
                k += 1

        # 全点 0 のアレイを作成
        intY = np.zeros((len(mskYY), len(datL)), np.float64)
        # 縦横を逆にして取り込む
        for i in range(len(mskYY)):
            for j in range(len(datL)):
                intY[i][j] = datL[j][i]
                if erL[j][i] < 0.0:
                    intY[i][j] = MASKVALUE

        # マップデータ用の X Y グリッドを作る
        X, Y = self._MakeInelasticPowderGrid(Ei, ebin, thetas)

        return (intY, X, Y)

    #########################################
    def _MakeInelasticPowderGrid(self, Ei, ebin, thetas):
        """
        XグリッドとYグリッドを計算
        @param Ei
        @param  ebin　エネルギーbin
        @param thetas 散乱角
        @retval XグリッドとYグリッド
        """

        x = np.arange(0.0, len(thetas), 1.0)
        y = np.array(ebin)

        X, Y = np.meshgrid(x, y)

        ki = math.sqrt(Ei / 2.072)
        ki2 = ki * ki

        for i in range(len(ebin)):

            hw = ebin[i]
            j = 0
            for theta in thetas:
                # 角度とエネルギーから散乱後の波数ベクトル計算
                kf = math.sqrt((Ei - hw) / 2.072)
                # Q を計算
                q = math.sqrt(ki2 + (kf * kf) - (2.0 * ki * kf * math.cos(theta)))
                X[i][j] = q
                j += 1

        return (X, Y)
#######################################
def format_int_range(range0, range1):
    """強度積算範囲設定処理

    引数のどちらかがNoneの時は全範囲になるようにNoneにする。

    Parameters:
        range0 (float or None): 積算範囲始値
        range1 (float or None): 積算範囲終値
    """
    if (range0 is None) or (range1 is None):
        return None
    # 浮動小数点に変換
    try:
        rg0 = float(range0)
        rg1 = float(range1)
    except:
        raise PlotException('Common', 'C027', ('range',))

    if rg1 >= rg0:
        return [rg0, rg1]
    else:
        return [rg1, rg0]
