Exemplo n.º 1
0
class graphManager(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=u"ReGenesis", pos=wx.DefaultPosition,
                          size=wx.Size(MAXWIDTH, MAXHEIGHT),
                          style=wx.DEFAULT_FRAME_STYLE & ~ (wx.RESIZE_BORDER |
                                                            wx.MAXIMIZE_BOX))
        self.SetSizeHints(wx.DefaultSize, wx.DefaultSize)

        self.sizer = wx.BoxSizer(wx.VERTICAL)

        self.renderer = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL)
        self.sizer.Add(self.renderer, 1, wx.EXPAND | wx.ALL, 5)


        # toolbar
        self.toolbar = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL)
        self.toolbar.SetMinSize(wx.Size(1200, 40))
        self.toolbar.SetMaxSize(wx.Size(1200,40))
        self.toolbarSizer = wx.BoxSizer(wx.HORIZONTAL)

        # adding buttons
        self.homeButton = wx.Button(self.toolbar, wx.ID_ANY, u"Reset", wx.DefaultPosition, wx.DefaultSize, 0)
        self.toolbarSizer.Add(self.homeButton, 0, wx.ALL, 5)

        self.backButton = wx.Button(self.toolbar, wx.ID_ANY, u"Undo", wx.DefaultPosition, wx.DefaultSize, 0)
        self.toolbarSizer.Add(self.backButton, 0, wx.ALL, 5)

        self.forwardButton = wx.Button(self.toolbar, wx.ID_ANY, u"Redo", wx.DefaultPosition, wx.DefaultSize, 0)
        self.toolbarSizer.Add(self.forwardButton, 0, wx.ALL, 5)

        self.panButton = wx.Button(self.toolbar, wx.ID_ANY, u"Pan", wx.DefaultPosition, wx.DefaultSize, 0)
        self.toolbarSizer.Add(self.panButton, 0, wx.ALL, 5)

        self.zoomButton = wx.Button(self.toolbar, wx.ID_ANY, u"Zoom", wx.DefaultPosition, wx.DefaultSize, 0)
        self.toolbarSizer.Add(self.zoomButton, 0, wx.ALL, 5)

        self.appearanceButton = wx.Button(self.toolbar, wx.ID_ANY, u"Edit Appearance", wx.DefaultPosition, wx.DefaultSize, 0)
        self.toolbarSizer.Add(self.appearanceButton, 0, wx.ALL, 5)

        self.saveButton = wx.Button(self.toolbar, wx.ID_ANY, u"Save", wx.DefaultPosition, wx.DefaultSize, 0)
        self.toolbarSizer.Add(self.saveButton, 0, wx.ALL, 5)

        self.loadButton = wx.Button(self.toolbar, wx.ID_ANY, u"Load", wx.DefaultPosition, wx.DefaultSize, 0)
        self.toolbarSizer.Add(self.loadButton, 0, wx.ALL, 5)

        self.exportButton = wx.Button(self.toolbar, wx.ID_ANY, u"Export", wx.DefaultPosition, wx.DefaultSize, 0)
        self.toolbarSizer.Add(self.exportButton, 0, wx.ALL, 5)



        self.toolbar.SetSizer(self.toolbarSizer)
        self.toolbar.Layout()
        self.toolbarSizer.Fit(self.toolbar)
        self.sizer.Add(self.toolbar, 1, wx.EXPAND | wx.ALL, 5)

        self.SetSizer(self.sizer)
        self.Layout()
        self.MenuBar = wx.MenuBar(0)

        #new graph menu options
        self.newGraph = wx.Menu()
        self.newPCA = wx.MenuItem(self.newGraph, wx.ID_ANY, u"PCA Graph", wx.EmptyString, wx.ITEM_NORMAL)
        self.newGraph.Append(self.newPCA)

        self.newAdmixture = wx.MenuItem(self.newGraph, wx.ID_ANY, u"Admixture Graph", wx.EmptyString, wx.ITEM_NORMAL)
        self.newGraph.Append(self.newAdmixture)

        self.MenuBar.Append(self.newGraph, u"New Graph")



        #Manage Graphs Menu
        self.manageGraphs = wx.Menu()

        self.saveGraph = wx.MenuItem(self.manageGraphs, wx.ID_ANY, u"Save", wx.EmptyString, wx.ITEM_NORMAL)
        self.manageGraphs.Append(self.saveGraph)

        self.loadGraph = wx.MenuItem(self.manageGraphs, wx.ID_ANY, u"Load", wx.EmptyString, wx.ITEM_NORMAL)
        self.manageGraphs.Append(self.loadGraph)

        self.exportGraph = wx.MenuItem(self.manageGraphs, wx.ID_ANY, u"Export", wx.EmptyString, wx.ITEM_NORMAL)
        self.manageGraphs.Append(self.exportGraph)

        self.MenuBar.Append(self.manageGraphs, u"Manage Graphs")

        self.SetMenuBar(self.MenuBar)

        self.Centre(wx.BOTH)





        # Connect Events
        # New graph events
        self.Bind(wx.EVT_MENU, self.newPCAOnMenuSelection, id=self.newPCA.GetId())
        self.Bind(wx.EVT_MENU, self.newAdmixtureOnMenuSelection, id=self.newAdmixture.GetId())

        # Manage graph events
        self.Bind(wx.EVT_MENU, self.saveGraphOnMenuSelection, id=self.saveGraph.GetId())
        self.Bind(wx.EVT_MENU, self.loadGraphOnMenuSelection, id=self.loadGraph.GetId())
        self.Bind(wx.EVT_MENU, self.exportGraphOnMenuSelection, id=self.exportGraph.GetId())

        # Toolbar Events

        self.saveButton.Bind(wx.EVT_BUTTON, self.saveGraphOnMenuSelection)
        self.loadButton.Bind(wx.EVT_BUTTON, self.loadGraphOnMenuSelection)
        self.exportButton.Bind(wx.EVT_BUTTON, self.onExportButtonClick)
        self.zoomButton.Bind(wx.EVT_BUTTON, self.onZoomButtonClick)
        self.appearanceButton.Bind(wx.EVT_BUTTON, self.onAppearanceButtonClick)
        self.homeButton.Bind(wx.EVT_BUTTON, self.onHomeButtonClick)
        self.panButton.Bind(wx.EVT_BUTTON, self.onPanButtonClick)
        self.forwardButton.Bind(wx.EVT_BUTTON, self.onForwardButtonClick)
        self.backButton.Bind(wx.EVT_BUTTON, self.onBackButtonClick)

        # Variable to store which graph type is currently loaded for saving  purposes
        self.graphType = None

    def __del__(self):
        pass

    def createFigure(self):
        """
               Creates figure or clears figure and adds a new subplot to the figure.


        """
        # checking if figure exists to avoid reinitializing

        if not hasattr(self, 'figure'):
            self.figure = mpl.figure.Figure(figsize=(FIGUREWIDTH, FIGUREHEIGHT), dpi=FIGUREDPI)

        else:
            self.figure.clf()

        # add subplot to newly created/cleared figure

        self.axes = self.figure.add_subplot(111)

    def showGraph(self):
        """
            Produces key and canvas.

        """
        self.axes.legend()
        self.canvas = FigureCanvas(self.renderer, wx.ID_ANY, self.figure)

        # Initialises navToolbar instance to use built in functions for custom toolbar
        if not hasattr(self, "navToolbar"):
            locale = wx.Locale(wx.LANGUAGE_ENGLISH)
            self.navToolbar = NavigationToolbar(self.canvas)
            self.navToolbar.Hide()

    def CreatePCAPlot(self, data):
        """
        Creates pca graph and draws it.
        """
        self.graph = pcaGraph(data)
        pcaData = self.graph.findPcaData(True)
        self.plotPcaData(pcaData)

    def CreateAdmixturePlot(self, data):
        """
        Creates admixture graph and draws it.
        """
        self.graph = admixGraph(data)
        admixData = self.graph.genDataDictionary()
        self.plotAdmixData(admixData)

    def plotPcaData(self, pcaData):
        """
        Plots PCA data to figure.
        """


        pcaAppearance.groupNames = self.graph.getGroups()
        pcaAppearance.groupColours = self.graph.getColours()
        pcaAppearance.groupShapes = self.graph.getShapes()
        pcaAppearance.groupSizes = self.graph.getSize()
        pcaAppearance.title = self.graph.getTitle()
        pcaAppearance.hasGrid = self.graph.getHasGrid()
        pcaAppearance.hasLabels = self.graph.getHasLabels()

        self.createFigure()

        for group in pcaData:
            x = pcaData[group]['x']
            y = pcaData[group]['y']
            # getting the colours of the groups
            colourList = self.graph.getColours()
            # getting the shapes of the groups
            shapeList = self.graph.getShapes()
            #getting the sizes of the groups
            sizeList = self.graph.getSize()
            #getting the title of the graph
            title = self.graph.getTitle()

            hasGrid = self.graph.getHasGrid()
            hasLabels = self.graph.getHasLabels()

            # plotting the graph
            self.axes.scatter(x, y, label=group, s=sizeList[group], color=colourList[group], marker=shapeList[group])
        self.axes.set_title(title)
        if(hasGrid):
            self.axes.grid()

        if(hasLabels):
            self.axes.set_xlabel(self.graph.getXLabel())
            self.axes.set_ylabel(self.graph.getYLabel())
        self.showGraph()

        return


    def plotAdmixData(self, admixData):
        """
        Plots admixture data to figure.
        """

        groups = list(admixData.keys())
        ancestryLabels = list(admixData[groups[0]].keys())
        individualCount = 0
        groupCenters = []

        # creating figure
        self.createFigure()

        fullRatios = []

        self.axes.set_xticklabels([])
        for group in admixData:

            ratios = []

            for ancestry in admixData[group]:
                anc = admixData[group][ancestry]
                ratios.append(anc)
            numAncestries = len(ratios)
            numIndividuals = len(ratios[0])

            # Normalising ratios so they add up to 1
            tempRatios = list(zip(*ratios))
            sums = list(map(sum, tempRatios))
            for ancestry in ratios:
                for i in range(len(ancestry)):
                    ancestry[i] /= sums[i]

            # storing values used for separating groups at presentation

            groupCenters.append(individualCount + (numIndividuals/2))
            individualCount += numIndividuals
            self.axes.axvline(individualCount, color='w')

            if len(fullRatios) > 0:
                for i in range(len(fullRatios)):
                    fullRatios[i].extend(ratios[i])
            else:
                fullRatios = ratios


        # plotting bars
        indexes = [i for i in range(individualCount)]
        self.axes.bar(indexes, fullRatios[0], 1, label=ancestryLabels[0])
        # barBottom keeps track of the current height of each bar so subsequent bars can be plotted above
        barBottom = [0 for i in range(individualCount)]
        for i in range(0, numAncestries - 1):

            # increasing height of barBottom
            barBottom = [x + y for x, y in zip(barBottom, fullRatios[i])]
            self.axes.bar(indexes, fullRatios[i + 1], 1, bottom=barBottom, label=ancestryLabels[i + 1])

        # setting labels for better readability
        self.axes.set_xticks(groupCenters)
        self.axes.set_xticklabels(groups)

        self.showGraph()

    # Event Handlers
    def newPCAOnMenuSelection(self, event):
        """
        Event handler for new PCA menu selection.
        """
        self.child = pcaCreator(self)
        self.Disable()
        self.child.ShowModal()
        if self.child.result == "CANCEL":
            event.Skip()
        elif self.child.result == "CONFIRM":
            self.CreatePCAPlot(self.child._dataDict)


    def newAdmixtureOnMenuSelection(self, event):
        """
        Event handler for new Admixture menu selection.
        """
        self.child = admixCreator(self)
        self.Disable()
        self.child.ShowModal()
        if self.child.result == "CANCEL":
            event.Skip()
        elif self.child.result == "CONFIRM":
            self.CreateAdmixturePlot(self.child._dataDict)

    def saveGraphOnMenuSelection(self, event):
        """
        Event handler for save Graph menu selection.
        """
        with wx.FileDialog(self, "Save Graph file", wildcard="Regenesis Graph File files (*.rgf)|*.rgf",
                           style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as fileDialog:

            if fileDialog.ShowModal() == wx.ID_CANCEL:
                return  # the user changed their mind

            # save the current contents in the file
            pathname = fileDialog.GetPath()
            try:
                with open(pathname, 'w', encoding='utf-8') as file:
                    self.doSaveData(file)
            except IOError:
                wx.LogError("Cannot save current data in file '%s'." % pathname)

    def doSaveData(self, f):
        """
        Saves project state to file.
        """
        # find dictionary of values to plot
        if self.graph.getGraphType() == 'admix':
            data = self.graph.getSaveFileData()
            # Add a graph type key to the data dict
            data.update({"GraphType" : 'admix'})
        elif self.graph.getGraphType() == 'pca':
            data = self.graph.getSaveFileData()
            # Add a graph type key to the data dict
            data.update({"GraphType" : "pca"})
        json.dump(data, f, ensure_ascii=False)
        f.close()

    def loadGraphOnMenuSelection(self, event):

        """
        Event handler for load Graph menu selection.
        """
            # otherwise ask the user what new file to open
        with wx.FileDialog(self, "Load Graph file", wildcard="Regenesis Graph File files (*.rgf)|*.rgf",
                           style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog:

            if fileDialog.ShowModal() == wx.ID_CANCEL:
                return  # the user changed their mind

            # Proceed loading the file chosen by the user
            pathname = fileDialog.GetPath()
            try:
                with open(pathname, 'r') as file:
                    self.doLoadData(file)
            except IOError:
                wx.LogError("Cannot open file '%s'." % file)

    def doLoadData(self,f):
        """
        Loads project state from file.
        """
        data = json.load(f)
        if data.get("GraphType") == 'admix':
            # Delete the Graph Type key from the data dict
            del data["GraphType"]
            self.CreateAdmixturePlot(data)
        elif data.get("GraphType") == 'pca':
            # Delete the Graph Type key from the data dict
            del data["GraphType"]
            self.CreatePCAPlot(data)
        f.close()

    def exportGraphOnMenuSelection(self, event):
        """
        Exports graph as image.
        """
        self.navToolbar.save_figure()

    def onZoomButtonClick(self, event):
        """
        Activates zoom mode on graph.
        """
        self.navToolbar.zoom()

    def onAppearanceButtonClick(self, event):
        """
        Opens appearance editor.
        """
        if self.graph.getGraphType() == 'pca':
            self.child = pcaAppearance(self)
            self.Disable()
            self.child.ShowModal()
        else:
            msgDlg = wx.MessageDialog(parent=self, message="Appearance editing functionality for admixture graph still to come in future releases",
                                      caption="Edit Admixture graph appearance", style=wx.ICON_INFORMATION)
            val = msgDlg.ShowModal()
            msgDlg.Show()


        if self.child.result == "CANCEL":
            event.Skip()
        elif self.child.result == "CONFIRM":

            if self.graph.getGraphType() == 'pca':
                #re-plotting the graph with the newly chosen colours
                newColours = self.child.GetColours()
                self.graph.setColours(newColours)

                #re-plotting the graph with the newly chosen shape
                newShapes = self.child.GetShapes()
                self.graph.setShapes(newShapes)

                #re-plotting the graph with the new group size
                newSize = self.child.GetSize()
                self.graph.setSize(newSize)

                #re-plotting graph with new title
                newTitle = self.child.GetTitle()
                self.graph.setTitle(newTitle)

                #re-plotting graph with grid settings
                grid = self.child.GetHasGrid()
                self.graph.setHasGrid(grid)

                #re-plotting graph with labels settings
                labels = self.child.GetHasLabels()
                self.graph.setHasLabels(labels)

                pcaData = self.graph.findPcaData(False)
                self.plotPcaData(pcaData)
                #self.CreatePCAPlot(self.child._dataDict)

        elif self.graph.getGraphType() == "admix":
            wx.MessageDialog(None, "Admixture Appearance editing to b")

    def onHomeButtonClick(self, event):
        """
        Resets graph panning and zooming
        """
        self.navToolbar.home()

    def onPanButtonClick(self, event):
        """
        Activates Pan mode on graph.
        """
        self.navToolbar.pan()

    def onConfigureButtonClick(self, event):
        self.navToolbar.configure_subplots()

    def onBackButtonClick(self, event):
        """
        Undoes latest panning or zooming change.
        """
        self.navToolbar.back()

    def onForwardButtonClick(self, event):
        """
        Redoes latest undone panning or zooming change.
        """
        self.navToolbar.forward()

    def onExportButtonClick(self, event):
        """
        Exports graph as image.
        """
        self.navToolbar.save_figure()
Exemplo n.º 2
0
class RTPWindow(wx.Frame):

    #########################Init Funcions#############################

    def __init__(self,parent=None,dpi=100,geoid=Geodesic.WGS84,resolution="50m",center_lon=0.,fontsize=8, verbose=False):
        """Constructor"""
        #call init of super class
        default_style = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.NO_FULL_REPAINT_ON_RESIZE | wx.WS_EX_CONTEXTHELP | wx.FRAME_EX_CONTEXTHELP
        wx.Frame.__init__(self, parent, title="Pole Plot %s"%parent.__version__,style=default_style, size=(600*2,600*2))
        self.Bind(wx.EVT_CLOSE, self.on_close_main)

        self.parent=parent
        if not pymax_found: self.parent.user_warning("PyRot module PyMax not found, pole plot viewer will not be usable"); self.on_close_window(-1)
        else: self.parent.rtp_open=True
        self.center_lon = center_lon
        self.dpi = dpi
        self.geoid = geoid
        self.resolution = resolution
        self.fontsize = fontsize
        self.verbose = verbose
        self.poles_to_plot = []

        self.panel = wx.Panel(self,-1,size=(400*2,300*2))

        #Populate UI and Menu
        self.init_UI()
        self.create_menu()
        self.configure()

        self.update()

    def init_UI(self):
        spacing = 10

        #------------------------------------Make DropDown Box-----------------------------------------------------#

        latlon_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, "Window Boundaries"), wx.VERTICAL)
        proj_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, "Choose Projection"), wx.VERTICAL)
        refresh_sizer = wx.StaticBoxSizer(wx.StaticBox(self.panel, wx.ID_ANY, "Refresh Figure"), wx.HORIZONTAL)

        projs = ["North Polar Stereographic","South Polar Stereographic","Orthographic"]
        self.proj_box = wx.ComboBox(self.panel, id=wx.ID_ANY,size=(100, 25), value=projs[0], choices=projs, style=wx.CB_DROPDOWN|wx.TE_READONLY)
        self.Bind(wx.EVT_COMBOBOX, self.on_select_proj,self.proj_box)

        self.max_lat_box = wx.TextCtrl(self.panel, id=wx.ID_ANY|wx.TE_CENTRE, size=(25,25))
        self.min_lat_box = wx.TextCtrl(self.panel, id=wx.ID_ANY|wx.TE_CENTRE, size=(25,25))
        self.max_lon_box = wx.TextCtrl(self.panel, id=wx.ID_ANY|wx.TE_CENTRE, size=(25,25))
        self.min_lon_box = wx.TextCtrl(self.panel, id=wx.ID_ANY|wx.TE_CENTRE, size=(25,25))
#        self.down_sample_box = wx.TextCtrl(self.panel, id=wx.ID_ANY|wx.TE_CENTRE, size=(50,25))\

        self.re_render_button = wx.Button(self.panel, id=wx.ID_ANY, label='Refresh Figure',size=(50,25))
        self.Bind(wx.EVT_BUTTON, self.on_re_render_button, self.re_render_button)
        self.add_pole_button = wx.Button(self.panel, id=wx.ID_ANY, label='Add Pole',size=(50,25))
        self.Bind(wx.EVT_BUTTON, self.on_add_pole_button, self.add_pole_button)

        #Projection sizer
        proj_sizer.Add(self.proj_box, 1, wx.ALIGN_LEFT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing)

        #Lat-Lon Sizer
        lat_sizer = wx.BoxSizer(wx.HORIZONTAL)
        lat_sizer.AddMany([(self.min_lat_box, 1, wx.ALIGN_LEFT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing),
                           (self.max_lat_box, 1, wx.ALIGN_RIGHT|wx.ALIGN_TOP|wx.EXPAND|wx.ALL, spacing)])
        lon_sizer = wx.BoxSizer(wx.HORIZONTAL)
        lon_sizer.AddMany([(self.min_lon_box, 1, wx.ALIGN_LEFT|wx.ALIGN_BOTTOM|wx.EXPAND|wx.ALL, spacing),
                           (self.max_lon_box, 1, wx.ALIGN_RIGHT|wx.ALIGN_BOTTOM|wx.EXPAND|wx.ALL, spacing)])
        latlon_sizer.AddMany([(lat_sizer, 1, wx.ALIGN_TOP|wx.EXPAND, spacing),
                              (lon_sizer, 1, wx.ALIGN_BOTTOM|wx.EXPAND, spacing)])

        #Downsample sizer with downsample box and refresh button
        refresh_sizer.AddMany([(self.re_render_button, 1, wx.ALIGN_LEFT|wx.ALIGN_BOTTOM|wx.EXPAND|wx.ALL, spacing),
                               (self.add_pole_button, 1, wx.ALIGN_RIGHT|wx.ALIGN_BOTTOM|wx.EXPAND|wx.ALL, spacing)])

        #Combine projection and downsample sizers
        proj_ds_sizer = wx.BoxSizer(wx.VERTICAL)
        proj_ds_sizer.AddMany([(proj_sizer, 1, wx.ALIGN_TOP|wx.EXPAND, spacing),
                               (refresh_sizer, 1, wx.ALIGN_BOTTOM|wx.EXPAND, spacing)])

        #Combine all in final sizer
        all_txt_btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
        all_txt_btn_sizer.AddMany([(proj_ds_sizer, 1, wx.ALIGN_LEFT|wx.EXPAND, spacing),
                                   (latlon_sizer, 1, wx.ALIGN_RIGHT|wx.EXPAND, spacing)])

        #-------------------------------------Make Figure----------------------------------------------------------#

        self.fig = Figure((2, 2), dpi=self.dpi)
        self.canvas = FigCanvas(self.panel, -1, self.fig)
        self.toolbar = NavigationToolbar(self.canvas)
        self.ax = self.fig.add_subplot(111)
        psk.remove_axis_lines_and_ticks(self.ax)
        self.toolbar.Hide()
        self.plot_setting = "Zoom"
        self.toolbar.zoom()
        self.canvas.Bind(wx.EVT_MIDDLE_DOWN,self.on_middle_click_plot)
#        self.canvas.Bind(wx.EVT_MOTION,self.on_move_mouse_plot)
        self.canvas.Bind(wx.EVT_LEFT_DCLICK, self.on_select_dleft_click)
        self.canvas.Bind(wx.EVT_RIGHT_DCLICK, self.on_select_dright_click)

        #----------------------------------Build UI and Fit--------------------------------------------------------#

        outer_sizer = wx.BoxSizer(wx.VERTICAL)
        outer_sizer.AddMany([#(grd_sizer,1,wx.ALIGN_CENTER|wx.ALIGN_TOP|wx.EXPAND|wx.LEFT|wx.RIGHT,spacing),
                             (all_txt_btn_sizer,1,wx.ALIGN_CENTER|wx.ALIGN_TOP|wx.EXPAND),
                             (self.canvas,10,wx.ALIGN_CENTER|wx.ALIGN_TOP|wx.EXPAND)])

        self.panel.SetSizerAndFit(outer_sizer)

    def create_menu(self):
        """
        Generates Menu
        """

        self.menubar = wx.MenuBar()

        #-----------------
        # File Menu
        #-----------------

        menu_file = wx.Menu()

        m_import_poles = menu_file.Append(-1, "&Import Poles From CSV", "ImportPoles")
        self.Bind(wx.EVT_MENU, self.on_import_poles, m_import_poles)

        menu_file.AppendSeparator()
        submenu_save_plots = wx.Menu()

        m_save_plot = submenu_save_plots.Append(-1, "&Save Plot", "")
        self.Bind(wx.EVT_MENU, self.on_save_plot, m_save_plot,"save-plot")

        m_new_sub_plots = menu_file.Append(-1, "&Save Result", submenu_save_plots)

        menu_file.AppendSeparator()
        m_exit = menu_file.Append(-1, "&Exit\tCtrl-Q", "Exit")
        self.Bind(wx.EVT_MENU, self.on_close_main, m_exit)

        #-----------------
        # Edit Menu
        #-----------------

        menu_edit = wx.Menu()

        self.m_remove_pole = menu_edit.Append(-1, "&Remove Last Pole\tCtrl-Z", "RemovePole")
        self.Bind(wx.EVT_MENU, self.on_remove_pole, self.m_remove_pole)

        self.m_remove_all_poles = menu_edit.Append(-1, "&Remove All Poles\tCtrl-Z", "RemovePolesAll")
        self.Bind(wx.EVT_MENU, self.on_remove_all_poles, self.m_remove_all_poles)

        self.m_add_strike_unc = menu_edit.AppendCheckItem(-1, "&Add Strike Uncertainty\tCtrl-R", "CalcStrikes")
        self.Bind(wx.EVT_MENU, self.on_add_strike_unc, self.m_add_strike_unc)

        self.m_solve_askw = menu_edit.AppendCheckItem(-1, "&Solve for Anomalous Skewness\tCtrl-A-R", "SolveAskw")
        self.Bind(wx.EVT_MENU, self.on_solve_askw, self.m_solve_askw)

        #-----------------
        # View Menu
        #-----------------

        menu_view = wx.Menu()

        self.m_show_lunes = menu_view.AppendCheckItem(-1, "&Show Lunes", "ShowLunes")
        self.m_show_lunes.Check()
        self.Bind(wx.EVT_MENU, self.on_show_lunes, self.m_show_lunes)

        self.m_show_pole = menu_view.AppendCheckItem(-1, "&Show Pole", "ShowPole")
#        self.m_show_pole.Check()
        self.Bind(wx.EVT_MENU, self.on_show_pole, self.m_show_pole)

        self.m_show_a95 = menu_view.AppendCheckItem(-1, "&Show A95", "ShowA95")
        self.m_show_a95.Check()
        self.Bind(wx.EVT_MENU, self.on_show_a95, self.m_show_a95)

        self.m_show_selected = menu_view.AppendCheckItem(-1, "&Show Selected", "ShowSelected")
        self.m_show_selected.Check()
        self.Bind(wx.EVT_MENU, self.on_show_selected, self.m_show_selected)

        #-----------------

        self.menubar.Append(menu_file, "&File")
        self.menubar.Append(menu_edit, "&Edit")
        self.menubar.Append(menu_view, "&View")
        self.SetMenuBar(self.menubar)

    def configure(self):
        self.min_lat_box.SetValue("%.1f"%60.)
        self.max_lat_box.SetValue("%.1f"%90.)
        self.min_lon_box.SetValue("%.1f"%-180.)
        self.max_lon_box.SetValue("%.1f"%180.)
        self.window = [None,None,None,None]

    #########################Update UI Funcions#############################

    def update(self): #Populates Logger and makes plot
        self.make_map() #Make Background Map

        if self.m_show_pole.IsChecked():

            if len(self.parent.deskew_df[self.parent.deskew_df["track_type"]=="ship"]) > 0:
                self.parent.save_max_file(".tmp.max",ship_only=True) #Save tmp max file to disk for debugging purposes and to more easily punch data into format using previous functions
                comment,header,ship_data = pymax.read_max_file(".tmp.max") #Read max file
                if len(ship_data["phs"])>2: #If more than 2 profiles (even-determined) invert ship
                    (plat,plon,pmag,maj_se,min_se,phi),chisq,dof = pymax.max_likelihood_pole(ship_data, trial_pole=header[:3], out_path="synth_mag_gui.maxout", save_full_data_kernel=self.verbose, step=header[-1], max_steps=100, comment=comment)
                    s1_ship = np.sqrt(chisq/dof)*ship_data["phs"][0][1][1] #ship 1sigma
                else: s1_ship = 0
            else: ship_data,s1_ship = {"phs":[["none",[0.,0.]]]},0

            if len(self.parent.deskew_df[self.parent.deskew_df["track_type"]=="aero"]) > 0:
                self.parent.save_max_file(".tmp.max",aero_only=True) #Do same for aero only data
                comment,header,aero_data = pymax.read_max_file(".tmp.max")
                if len(aero_data["phs"])>2:
                    (plat,plon,pmag,maj_se,min_se,phi),chisq,dof = pymax.max_likelihood_pole(aero_data, trial_pole=header[:3], out_path="synth_mag_gui.maxout", save_full_data_kernel=self.verbose, step=header[-1], max_steps=100, comment=comment)
                    s1_aero = np.sqrt(chisq/dof)*aero_data["phs"][0][1][1]
                else: s1_aero = 0
            else: aero_data,s1_aero = {"phs":[["none",[0.,0.]]]},0

            self.parent.save_max_file(".tmp.max") #now read all data and change s1 to match above
            comment,header,data = pymax.read_max_file(".tmp.max")
            if len(data["phs"])==0: return
            for i in range(len(data["phs"])):
                if len(ship_data["phs"]) > 0 and data["phs"][i][1][1]==ship_data["phs"][0][1][1]:
                    data["phs"][i][1][1] = s1_ship
                elif len(aero_data["phs"]) > 0 and data["phs"][i][1][1]==aero_data["phs"][0][1][1]:
                    data["phs"][i][1][1] = s1_aero

            if self.m_solve_askw.IsChecked(): (plat,plon,pmag,askw,maj_se,min_se,phi),chisq,dof = pymax.max_likelihood_pole(data, trial_pole=header[:3], out_path="synth_mag_gui.maxout", save_full_data_kernel=self.verbose, step=header[-1], max_steps=100, comment=comment, solve_anom_skew=self.m_solve_askw.IsChecked())
            else: (plat,plon,pmag,maj_se,min_se,phi),chisq,dof = pymax.max_likelihood_pole(data, trial_pole=header[:3], out_path="synth_mag_gui.maxout", save_full_data_kernel=self.verbose, step=header[-1], max_steps=100, comment=comment, solve_anom_skew=self.m_solve_askw.IsChecked())
            if self.m_add_strike_unc.IsChecked(): #If strike unc is to be included calculate it!!!
                (maj_se,min_se,phi) = cs.calc_strikes_and_add_err(self.parent.deskew_path,mlat=plat,mlon=plon,ma=maj_se,mb=min_se,mphi=phi,geoid=self.geoid,outfile=".tmp_dsk_cs",filter_by_quality=False,visualize=False,convergence_level=1e-5)
                os.remove(".tmp_dsk_cs")

            #write pole coordinates and 1sigmas to plot for user
            if phi<0: phi = phi+180
            elif phi>180: phi = phi%180
            if self.m_show_a95.IsChecked():
                f_factor = f.ppf(.95,2,dof)
                print(f_factor)
                maj_se,min_se = maj_se*np.sqrt(f_factor),min_se*np.sqrt(f_factor)
            if self.m_solve_askw.IsChecked(): self.ax.annotate(r"%.1f$^\circ$N, %.1f$^\circ$E"%(plat,plon)+"\n"+r"%.1f$^\circ$, %.1f$^\circ$, N%.1fE"%(maj_se,min_se,phi)+"\n"+"Anom. Skw. = %.1f"%askw+"\n"+r"$\chi^2_\nu$ = %.2f"%(chisq/dof)+"\n"+r"$1\sigma_{aero}$=%.1f"%(s1_aero)+"\n"+r"$1\sigma_{ship}$=%.1f"%(s1_ship),xy=(1-0.02,1-0.02),xycoords="axes fraction",bbox=dict(boxstyle="round", fc="w",alpha=.5),fontsize=self.fontsize,ha='right',va='top')
            else: self.ax.annotate(r"%.1f$^\circ$N, %.1f$^\circ$E"%(plat,plon)+"\n"+r"%.1f$^\circ$, %.1f$^\circ$, N%.1fE"%(maj_se,min_se,phi)+"\n"+r"$\chi^2_\nu$ = %.2f"%(chisq/dof)+"\n"+r"$1\sigma_{aero}$=%.1f"%(s1_aero)+"\n"+r"$1\sigma_{ship}$=%.1f"%(s1_ship),xy=(1-0.02,1-0.02),xycoords="axes fraction",bbox=dict(boxstyle="round", fc="w",alpha=.5),fontsize=self.fontsize,ha='right',va='top')
            #plot inverted pole
            self.ax = psk.plot_pole(plon,plat,phi,np.sqrt(chisq/dof)*maj_se,np.sqrt(chisq/dof)*min_se,m=self.ax, alpha=.5, zorder=10000)
        if self.m_show_lunes.IsChecked():
            #filter deskew_df to only data labeled "good" and plot lunes
            if self.m_solve_askw.IsChecked():
                srf,asf = self.parent.get_srf_asf()
                new_asf = lambda sr: asf(sr)+askw
                self.parent.deskew_df = sk.calc_aei(self.parent.deskew_df,srf,new_asf)
            else:
                self.parent.deskew_df = sk.calc_aei(self.parent.deskew_df,*self.parent.get_srf_asf())
            dsk_to_plot = self.parent.deskew_df[self.parent.deskew_df["quality"]=="g"]
            if self.m_show_selected.IsChecked():
                try: self.ax = psk.plot_lunes(dsk_to_plot,self.ax,idx_selected=self.parent.dsk_idx)
                except AttributeError: self.ax = psk.plot_lunes(dsk_to_plot,self.ax) #catch no selected data case
            else: self.ax = psk.plot_lunes(dsk_to_plot,self.ax)
#            os.remove(".tmp.max") #remove the deskew file on disk

        #plot any additional poles
        for pole_rec in self.poles_to_plot:
            print(pole_rec)
            self.ax = psk.plot_pole(*pole_rec[0],color=pole_rec[1],m=self.ax,zorder=1000)

        #set the map extent to match user input
        print([float(self.min_lon_box.GetValue()),float(self.max_lon_box.GetValue()),float(self.min_lat_box.GetValue()),float(self.max_lat_box.GetValue())])
        self.ax.set_extent([float(self.min_lon_box.GetValue()),float(self.max_lon_box.GetValue()),float(self.min_lat_box.GetValue()),float(self.max_lat_box.GetValue())], ccrs.PlateCarree())

        self.canvas.draw() #rerender

    def on_close_main(self,event):
        self.parent.rtp_open=False
        self.Destroy()

    ############################Menu Funcions################################

    def on_import_poles(self,event):
        dlg = wx.FileDialog(
            self, message="Choose CSV File",
            defaultDir=self.parent.WD,
            wildcard="Files (*.csv)|*.csv|All Files (*.*)|*.*",
            style=wx.FD_OPEN
            )
        if dlg.ShowModal() == wx.ID_OK:
            import_csv=dlg.GetPath()
            df_poles = pd.read_csv(import_csv,sep=None)
        dlg.Destroy()

        uniform_cols = list(map(lambda x: str(x).lower(), df_poles.columns))
        df_poles.columns = uniform_cols
        try:
            lat_col = next(filter(lambda x: x.startswith("lat"), uniform_cols))
            lon_col = next(filter(lambda x: x.startswith("lon"), uniform_cols))
            maj_col = next(filter(lambda x: x.startswith("maj"), uniform_cols))
            min_col = next(filter(lambda x: x.startswith("min"), uniform_cols))
            azi_col = next(filter(lambda x: x.startswith("azi"), uniform_cols))
        except:
            self.parent.user_warning("""Couldn't find a required column. There must be at least 5 columns.
                                        These 5 columns must have labels that start with lat, lon, maj, min, azi
                                        in any order and case insensitive. If more than one column fits these
                                        conditions then the first column is taken.""")
            return
        try: color_col = next(filter(lambda x: "color" in x, uniform_cols))
        except: color_col=None
        for i,row in df_poles.iterrows():
            if isinstance(color_col,type(None)):
                self.parent.user_warning("No Color for Pole (%.1f,%.1f), please specify"%(row[lat_col],row[lon_col]))
                cdlg = wx.ColourDialog(self)
                if cdlg.ShowModal() == wx.ID_OK:
                    color = tuple(np.array(cdlg.GetColourData().GetColour().Get())/255)
                else: color = "tab:blue"
            else: color = row[color_col]
            self.poles_to_plot.append([row[[lon_col,lat_col,azi_col,maj_col,min_col]].values,color])
        self.update()

    def on_save_plot(self,event):
        self.toolbar.save_figure()

    def on_remove_pole(self,event):
        self.poles_to_plot = self.poles_to_plot[:-1]
        self.update()

    def on_remove_all_poles(self,event):
        self.poles_to_plot = []
        self.update()

    def on_add_strike_unc(self,event):
        self.update()

    def on_solve_askw(self,event):
        self.update()

    def on_show_lunes(self,event):
        self.update()

    def on_show_pole(self,event):
        self.update()

    def on_show_a95(self,event):
        self.update()

    def on_show_selected(self,event):
        self.update()

    ###################Button and Dropdown Functions#########################

#    def on_change_grd_btn(self,event):
#        dlg = wx.FileDialog(
#            self, message="Choose Grid File",
#            defaultDir=self.parent.WD,
#            defaultFile="",
#            wildcard="Grid Files (*.grd,*.nc,*.ncf)|*.grd;*.nc;*.ncf|All Files (*.*)|*.*",
#            style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST
#            )
#        if dlg.ShowModal() == wx.ID_OK:
#            self.grd_file = dlg.GetPath()
#            self.grd_path.SetValue(self.grd_file)
#            self.update()
#            dlg.Destroy()
#        else: dlg.Destroy()

    def on_select_proj(self,event):
        self.update()

    def on_re_render_button(self,event):
        self.update()

    def on_add_pole_button(self,event):
        pdlg = PoleDialog(self) #run text entry dialog
        if pdlg.ShowModal() == wx.ID_OK:
            new_pole = [pdlg.lon,pdlg.lat,pdlg.phi,pdlg.a,pdlg.b]
        else: return
        pdlg.Destroy()
        cdata = wx.ColourData()
        cdata.SetChooseFull(True)
#        cdata.SetChooseAlpha(True)
        cdata.SetColour(wx.Colour(255, 0, 0, 128))
        cdlg = wx.ColourDialog(self,cdata)
        if cdlg.ShowModal() == wx.ID_OK:
            new_color = tuple(np.array(cdlg.GetColourData().GetColour().Get())/255)
        else: color = "#00FFFF88"
        if len(new_color)==3 or new_color[3]==1.: new_color =  (new_color[0],new_color[1],new_color[2],.5)
        elif len(new_color)<3:
            raise RuntimeError("If you're looking at this error in the terminal while running SynthMag GUI, you shouldn't be able to get here and something is significantly wrong with the color picker. Contact the dev on github.")
        cdlg.Destroy()
        self.poles_to_plot.append([new_pole,new_color]) #add new pole to list
        self.update() #update figure

    ###########################Figure Funcions###############################

    def on_middle_click_plot(self,event):
        if event.LeftIsDown() or event.ButtonDClick(): event.Skip(); return
        elif self.plot_setting == "Zoom":
            self.plot_setting = "Pan"
            self.toolbar.pan('off')
        elif self.plot_setting == "Pan":
            self.plot_setting = "Zoom"
            self.toolbar.zoom()
        event.Skip()

#    def on_move_mouse_plot(self,event):
#        try: dsk_row = self.parent.dsk_row
#        except AttributeError: event.Skip(); return
#        pos=event.GetPosition()
#        width, height = self.canvas.get_width_height()
#        pos = [pos[0],height-pos[1]]
#        pos = self.ax.transData.inverted().transform(pos)
#        lonlat = ccrs.PlateCarree().transform_point(*pos,self.proj)
#        self.plot_tracer_on_self_and_parent(dsk_row,lonlat)
#        self.parent.canvas.draw()
#        self.canvas.draw()
#        event.Skip()

    def on_select_dleft_click(self,event): #TODO make rtp
        try: self.parent.dsk_row
        except AttributeError: event.Skip(); return

        pos=event.GetPosition()
        width, height = self.canvas.get_width_height()
        pos = [pos[0],height-pos[1]]
        pos = self.ax.transData.inverted().transform(pos)

        plonlat = ccrs.PlateCarree().transform_point(*pos,self.proj)
        srf,asf = self.parent.get_srf_asf()
        reduced_skewness,rel_reduced_amplitude = sk.reduce_dsk_row_to_pole(self.parent.dsk_row,*plonlat,asf,srf)
        self.parent.phase_shift_box.SetValue("%.1f"%reduced_skewness)
        self.parent.deskew_df.at[self.parent.dsk_idx,'phase_shift'] = reduced_skewness
        self.parent.deskew_df.at[self.parent.dsk_idx,'rel_amp'] = rel_reduced_amplitude
        self.parent.dsk_row = self.parent.deskew_df.loc[self.parent.dsk_idx].iloc[0]

        self.parent.update(event)
        self.update()

    def on_select_dright_click(self,event): #TODO make rtp
        try: self.parent.deskew_df
        except AttributeError: event.Skip(); return

        pos=event.GetPosition()
        width, height = self.canvas.get_width_height()
        pos = [pos[0],height-pos[1]]
        pos = self.ax.transData.inverted().transform(pos)

        plonlat = ccrs.PlateCarree().transform_point(*pos,self.proj)
        srf,asf = self.parent.get_srf_asf()
        for i,row in self.parent.deskew_df.iterrows():
            reduced_skewness,rel_reduced_amplitude = sk.reduce_dsk_row_to_pole(row,*plonlat,asf,srf)
            self.parent.deskew_df.at[i,'phase_shift'] = reduced_skewness
            self.parent.deskew_df.at[i,'rel_amp'] = rel_reduced_amplitude
        self.parent.deskew_df = sk.calc_aei(self.parent.deskew_df,srf,asf)
        try: self.parent.dsk_row = self.parent.deskew_df.loc[self.parent.dsk_idx].iloc[0]
        except (AttributeError,KeyError) as e: pass

        self.parent.update(event)
        self.update()

    ##########################Additional Plotting and Backend Functions################

    def on_parent_select_track(self):
        self.update()

    def make_map(self):
        #set basemap
        try: self.fig.delaxes(self.ax)
        except AttributeError: self.parent.user_warning("Unable to remove previous axis and refresh map, raise issue with Dev.")
        #TODO: ADD TRANSVERSE MERCATOR AT STRIKE AS OPTION
        if self.proj_box.GetValue() == 'North Polar Stereographic':
            self.proj = ccrs.NorthPolarStereo(central_longitude=self.center_lon,true_scale_latitude=None,globe=None)
            self.ax = self.fig.add_subplot(111,projection=self.proj)
#            pgeo.make_circular_ax(self.ax)
        elif self.proj_box.GetValue() == 'South Polar Stereographic':
            self.proj = ccrs.SouthPolarStereo(central_longitude=self.center_lon,true_scale_latitude=None,globe=None)
            self.ax = self.fig.add_subplot(111,projection=self.proj)
#            pgeo.make_circular_ax(self.ax)
        elif self.proj_box.GetValue() == 'Orthographic':
            self.proj = ccrs.Orthographic(central_longitude=self.center_lon)
            self.ax = self.fig.add_subplot(111,projection=self.proj)
        else: self.parent.user_warning("Projection %s not supported"%str(self.proj_box.GetValue())); return

#        self.ax.set_xticks(np.arange(0, 370, 10.), crs=ccrs.PlateCarree())
#        self.ax.set_yticks(np.arange(-80, 90, 10.), crs=ccrs.PlateCarree())
#        self.ax.tick_params(grid_linewidth=.5,grid_linestyle=":",color="k",labelsize=8)
#        lon_formatter = LongitudeFormatter(zero_direction_label=True)
#        lat_formatter = LatitudeFormatter()
#        self.ax.xaxis.set_major_formatter(lon_formatter)
#        self.ax.yaxis.set_major_formatter(lat_formatter)
        self.ax.gridlines(color='grey', alpha=0.5, linestyle='--',linewidth=.5)
        land = cfeature.NaturalEarthFeature('physical', 'land', self.resolution, edgecolor="black", facecolor="grey", linewidth=2)
        self.ax.add_feature(land)
Exemplo n.º 3
0
class SkwLatWindow(wx.Frame):

    #########################Init Funcions#############################

    def __init__(self, parent=None, dpi=200, fontsize=6):
        """Constructor"""
        #call init of super class
        default_style = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.NO_FULL_REPAINT_ON_RESIZE | wx.WS_EX_CONTEXTHELP | wx.FRAME_EX_CONTEXTHELP
        wx.Frame.__init__(self,
                          parent,
                          title="Skewness by Latitude %s" % parent.__version__,
                          style=default_style)
        self.Bind(wx.EVT_CLOSE, self.on_close_main)

        self.parent = parent
        self.dpi = dpi
        self.fontsize = fontsize

        #        self.panel = wx.Panel(self, wx.ID_ANY)
        self.scrolled_panel = wx.lib.scrolledpanel.ScrolledPanel(
            self, wx.ID_ANY)  # make the Panel

        #Populate UI and Menu
        self.init_UI()
        self.create_menu()

        self.scrolled_panel.SetAutoLayout(True)
        self.scrolled_panel.SetupScrolling()  # endable scrolling

        self.update()

    def init_UI(self):
        spacing, vpadding, hpadding = 0., .01, .15

        #------------------------------------Make DropDown Box-----------------------------------------------------#

        sz_names_sizer = wx.StaticBoxSizer(
            wx.StaticBox(self.scrolled_panel, wx.ID_ANY, "Spreading Zone"),
            wx.HORIZONTAL)

        try:
            sz_names = self.parent.deskew_df["sz_name"].drop_duplicates(
            ).tolist()
            self.maximum_profiles = max([
                len(self.parent.deskew_df[self.parent.deskew_df["sz_name"] ==
                                          sz_name]) for sz_name in sz_names
            ])
        except AttributeError:
            sz_names, self.maximum_profiles = [""], 6
        self.sz_names_box = wx.ComboBox(self.scrolled_panel,
                                        id=wx.ID_ANY,
                                        size=(300, 50),
                                        value=sz_names[0],
                                        choices=sz_names,
                                        style=wx.CB_DROPDOWN | wx.TE_READONLY)
        self.Bind(wx.EVT_COMBOBOX, self.on_select_sz, self.sz_names_box)

        self.show_synth_button = wx.CheckBox(self.scrolled_panel,
                                             id=wx.ID_ANY,
                                             label="Plot Synthetic",
                                             size=(300, 50))
        self.Bind(wx.EVT_CHECKBOX, self.on_show_synth_button,
                  self.show_synth_button)

        sz_names_sizer.AddMany([
            (self.sz_names_box, 0, wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL
             | wx.ALIGN_LEFT | wx.EXPAND | wx.ALL, spacing),
            (self.show_synth_button, 0, wx.ALIGN_CENTER
             | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL,
             spacing)
        ])
        #        self.panel.SetSizerAndFit(sz_names_sizer)

        #-------------------------------------Make Figure----------------------------------------------------------#

        canvas_sizer = wx.BoxSizer(wx.VERTICAL)

        self.fig = Figure((2., 1. * self.maximum_profiles), dpi=self.dpi)
        self.fig.subplots_adjust(top=1. - vpadding,
                                 right=1. - hpadding,
                                 left=hpadding,
                                 bottom=vpadding,
                                 wspace=.0,
                                 hspace=.0)
        self.canvas = FigCanvas(self.scrolled_panel, wx.ID_ANY, self.fig)
        self.toolbar = NavigationToolbar(self.canvas)
        self.toolbar.Hide()
        self.plot_setting = "Zoom"
        self.toolbar.zoom()
        self.canvas.Bind(wx.EVT_MIDDLE_DOWN, self.on_middle_click_plot)
        self.canvas.Bind(wx.EVT_LEFT_DCLICK, self.on_select_dleft_click)

        canvas_sizer.AddMany([
            (sz_names_sizer, 0,
             wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND | wx.ALL,
             spacing),
            (self.canvas, 1,
             wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND | wx.ALL,
             spacing)
        ])

        #----------------------------------Build UI and Fit--------------------------------------------------------#

        self.scrolled_panel.SetSizerAndFit(canvas_sizer)

#        outer_sizer = wx.BoxSizer(wx.VERTICAL)
#        outer_sizer.AddMany([(self.panel,1,wx.ALIGN_CENTER|wx.EXPAND),
#                            (self.scrolled_panel,2,wx.ALIGN_CENTER|wx.ALIGN_TOP|wx.EXPAND)])

#        self.SetSizer(outer_sizer)
#        outer_sizer.Fit(self)

    def create_menu(self):
        """
        Generates Menu
        """

        self.menubar = wx.MenuBar()

        #-----------------
        # File Menu
        #-----------------

        menu_file = wx.Menu()

        submenu_save_plots = wx.Menu()

        m_save_plot = submenu_save_plots.Append(-1, "&Save Plot", "")
        self.Bind(wx.EVT_MENU, self.on_save_plot, m_save_plot, "save-plot")

        m_new_sub_plots = menu_file.Append(-1, "&Save Result",
                                           submenu_save_plots)

        menu_file.AppendSeparator()
        m_exit = menu_file.Append(-1, "&Exit\tCtrl-Q", "Exit")
        self.Bind(wx.EVT_MENU, self.on_close_main, m_exit)

        #-----------------

        self.menubar.Append(menu_file, "&File")
        self.SetMenuBar(self.menubar)

    #########################Update UI Funcions#############################

    def update(self):  #Populates Logger and makes plot
        self.fig.clear()
        self.plot_skewnesses_by_lat()
        self.canvas.draw()

    def on_save_plot(self, event):
        self.toolbar.save_figure()

    def on_close_main(self, event):
        self.parent.skw_lat_open = False
        self.Destroy()

    ##########################ComboBox Funcions##############################

    def on_select_sz(self, event):
        self.update()

    def on_show_synth_button(self, event):
        self.update()

    ###########################Figure Funcions###############################

    def on_middle_click_plot(self, event):
        if event.LeftIsDown() or event.ButtonDClick():
            event.Skip()
            return
        elif self.plot_setting == "Zoom":
            self.plot_setting = "Pan"
            self.toolbar.pan('off')
        elif self.plot_setting == "Pan":
            self.plot_setting = "Zoom"
            self.toolbar.zoom()
        event.Skip()

    def on_select_dleft_click(self, event):
        pass
#        try: dsk_df = self.parent.deskew_df
#        except AttributeError: event.Skip(); return

#        pos=event.GetPosition()
#        width, height = self.canvas.get_width_height()
#        pos = [pos[0],height-pos[1]]
#        pos = self.ax.transData.inverted().transform(pos)

#        min_dis,min_row = np.inf,None
#        ylim = self.ax.get_ylim()
#        for i,row in dsk_df.iterrows():
#            dis = ((row["inter_lat"]-pos[0])/ylim[0])**2 + ((row["aei"]-pos[1])/ylim[1])**2
#            if dis < min_dis:
#                min_dis = dis
#                min_row = row

#        self.parent.track_box.SetValue(min_row["comp_name"])
#        self.parent.on_select_track(event)

##########################Additional Plotting and Backend Functions################

    def on_parent_select_track(self):
        pass

    def plot_skewnesses_by_lat(self, clip_on=True):

        try:
            sz_name = self.sz_names_box.GetValue()
            rows = self.parent.deskew_df[self.parent.deskew_df["sz_name"] ==
                                         sz_name]
            rows.sort_values("inter_lat", inplace=True, ascending=False)
        except (AttributeError, KeyError) as e:
            print("Spreading Zone %s not found in deskew file" %
                  str(self.sz_names_box.GetValue()))
            return
        try:
            xlims = self.parent.ax.get_xlim()
            ylims = self.parent.ax.get_ylim()
        except AttributeError:
            xlims, ylims = (-300, 300), (-150, 150)
        axs = self.fig.subplots(self.maximum_profiles,
                                1,
                                sharex=True,
                                sharey=True)
        #        for ax in axs:
        #            ax.set_facecolor("grey")
        axs = axs[:len(rows)]

        for j, (ax, (i, row)) in enumerate(zip(axs, rows.iterrows())):
            print(j, row["comp_name"], xlims, ylims)
            ax.set_anchor('W')

            psk.remove_axis_lines_and_ticks(ax)

            min_proj_dis, max_proj_dis = psk.plot_skewness_data(
                row,
                float(row['phase_shift']),
                ax,
                picker=True,
                clip_on=clip_on,
                xlims=xlims,
                flip=True)

            ax.annotate(
                r"%s" % row['comp_name'] + "\n" +
                r"%.1f$^\circ$N,%.1f$^\circ$E" % (float(
                    row['inter_lat']), utl.convert_to_0_360(row['inter_lon'])),
                xy=(-.215, .5),
                xycoords="axes fraction",
                fontsize=self.fontsize,
                va="center",
                ha="left")
            ax.annotate(r"$\theta$=%.1f" % float(row['phase_shift']) + "\n" +
                        r"$e_a$=%.1f" % float(row['aei']),
                        xy=(1.15, .5),
                        xycoords="axes fraction",
                        fontsize=self.fontsize,
                        va="center",
                        ha="right")
            #            ax.set_ylabel(r"$\theta$=%.1f"%float(row['phase_shift'])+"\n"+r"$e_a$=%.1f"%float(row['aei']),rotation=0,fontsize=self.fontsize)
            ax.yaxis.set_label_coords(1.05, .45)
            ax.patch.set_alpha(0.0)
            #            ax.format_coord = format_coord

            if self.show_synth_button.GetValue():
                try:
                    ax.plot(self.parent.dis_synth,
                            self.parent.synth,
                            'r-',
                            alpha=.4,
                            zorder=1)
                except (AttributeError, IndexError):
                    print(
                        "No synthetic found to render in skewness by latitude window"
                    )

        scale = np.sqrt(sum(np.array(xlims)**2))
        if not scale < 20 or scale > 3000:
            ax.set_xlim(xlims)
            ax.set_ylim(ylims)

        if self.parent.spreading_rate_path != None:
            psk.plot_chron_span_on_axes(
                sz_name,
                self.fig.get_axes(),
                rows[['age_min', 'age_max']].iloc[0],
                spreading_rate_path=self.parent.spreading_rate_path)

#        self.fig.subplots_adjust(hspace=.0) #remove space between subplot axes

        self.canvas.draw()