Browse Source

wxgui: added new bivariate scatter plot tool; code refactoring for profile and histogram plots.

git-svn-id: https://svn.osgeo.org/grass/grass/trunk@48622 15284696-431f-4ddb-bdfa-cd5b030d7da7
Michael Barton 13 years ago
parent
commit
35812e0fdf

+ 29 - 4
gui/wxpython/gui_modules/mapdisp.py

@@ -56,6 +56,7 @@ from mapdisp_window  import BufferedWindow
 from histogram       import HistFrame
 from histogram       import HistFrame
 from wxplot          import HistFrame as HistFramePyPlot
 from wxplot          import HistFrame as HistFramePyPlot
 from wxplot          import ProfileFrame
 from wxplot          import ProfileFrame
+from wxplot          import ScatterFrame
 from grass.script import core as grass
 from grass.script import core as grass
 
 
 # for standalone app
 # for standalone app
@@ -1534,14 +1535,19 @@ class MapFrame(wx.Frame):
         profile = wx.MenuItem(toolsmenu, wx.ID_ANY, icons["profile"].GetLabel())
         profile = wx.MenuItem(toolsmenu, wx.ID_ANY, icons["profile"].GetLabel())
         profile.SetBitmap(icons["profile"].GetBitmap(self.iconsize))
         profile.SetBitmap(icons["profile"].GetBitmap(self.iconsize))
         toolsmenu.AppendItem(profile)
         toolsmenu.AppendItem(profile)
-        self.Bind(wx.EVT_MENU, self.Profile, profile)
+        self.Bind(wx.EVT_MENU, self.OnProfile, profile)
 
 
-        histogram2 = wx.MenuItem(toolsmenu, wx.ID_ANY, _("Create histogram with PyPlot"))
+        scatterplot = wx.MenuItem(toolsmenu, wx.ID_ANY, _("Create bivariate scatterplot of raster maps"))
+        scatterplot.SetBitmap(icons["profile"].GetBitmap(self.iconsize))
+        toolsmenu.AppendItem(scatterplot)
+        self.Bind(wx.EVT_MENU, self.OnScatterplot, scatterplot)
+
+        histogram2 = wx.MenuItem(toolsmenu, wx.ID_ANY, icons["histogram"].GetLabel())
         histogram2.SetBitmap(icons["histogram"].GetBitmap(self.iconsize))
         histogram2.SetBitmap(icons["histogram"].GetBitmap(self.iconsize))
         toolsmenu.AppendItem(histogram2)
         toolsmenu.AppendItem(histogram2)
         self.Bind(wx.EVT_MENU, self.OnHistogramPyPlot, histogram2)
         self.Bind(wx.EVT_MENU, self.OnHistogramPyPlot, histogram2)
 
 
-        histogram = wx.MenuItem(toolsmenu, wx.ID_ANY, icons["histogram"].GetLabel())
+        histogram = wx.MenuItem(toolsmenu, wx.ID_ANY, _("Create histogram with d.histogram"))
         histogram.SetBitmap(icons["histogram"].GetBitmap(self.iconsize))
         histogram.SetBitmap(icons["histogram"].GetBitmap(self.iconsize))
         toolsmenu.AppendItem(histogram)
         toolsmenu.AppendItem(histogram)
         self.Bind(wx.EVT_MENU, self.OnHistogram, histogram)
         self.Bind(wx.EVT_MENU, self.OnHistogram, histogram)
@@ -1633,7 +1639,7 @@ class MapFrame(wx.Frame):
         
         
         return dist
         return dist
 
 
-    def Profile(self, event):
+    def OnProfile(self, event):
         """!Init profile canvas and tools
         """!Init profile canvas and tools
         """
         """
         raster = []
         raster = []
@@ -1716,6 +1722,25 @@ class MapFrame(wx.Frame):
         # is selected to be histogrammed
         # is selected to be histogrammed
         self.histogramPyPlot.OnSelectRaster(None)
         self.histogramPyPlot.OnSelectRaster(None)
         
         
+    def OnScatterplot(self, event):
+        """!Init PyPlot scatterplot display canvas and tools
+        """
+        raster = []
+
+        for layer in self.tree.GetSelections():
+            if self.tree.GetPyData(layer)[0]['maplayer'].GetType() != 'raster':
+                continue
+            raster.append(self.tree.GetPyData(layer)[0]['maplayer'].GetName())
+
+        self.scatterplot = ScatterFrame(self, id = wx.ID_ANY, 
+                                                pos = wx.DefaultPosition, size = (700,300),
+                                                style = wx.DEFAULT_FRAME_STYLE, 
+                                                rasterList = raster)
+        self.scatterplot.Show()
+        # Open raster select dialog to make sure that at least 2 rasters (and the desired rasters)
+        # are selected to be plotted
+        self.scatterplot.OnSelectRaster(None)
+
     def OnHistogram(self, event):
     def OnHistogram(self, event):
         """!Init histogram display canvas and tools
         """!Init histogram display canvas and tools
         """
         """

File diff suppressed because it is too large
+ 987 - 2356
gui/wxpython/gui_modules/preferences.py


+ 40 - 0
gui/wxpython/gui_modules/toolbars.py

@@ -14,6 +14,7 @@ Classes:
  - ModelToolbar
  - ModelToolbar
  - HistogramToolbar
  - HistogramToolbar
  - Histogram2Toolbar
  - Histogram2Toolbar
+ - ScatterplotToolbar
  - LMWorkspaceToolbar
  - LMWorkspaceToolbar
  - LMDataToolbar
  - LMDataToolbar
  - LMToolsToolbar
  - LMToolsToolbar
@@ -1456,6 +1457,45 @@ class Histogram2Toolbar(AbstractToolbar):
                                       self.parent.OnQuit),
                                       self.parent.OnQuit),
                                      ))
                                      ))
 
 
+class ScatterplotToolbar(AbstractToolbar):
+    """!Toolbar for bivariate scatterplots of raster map pairs
+    """ 
+    def __init__(self, parent):
+        AbstractToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        icons = Icons['profile']
+        return self._getToolbarData((('addraster', Icons['layerManager']["addRast"],
+                                      self.parent.OnSelectRaster),
+                                     (None, ),
+                                     ('draw', icons["draw"],
+                                      self.parent.OnCreateScatter),
+                                     ('erase', Icons['displayWindow']["erase"],
+                                      self.parent.OnErase),
+                                     ('drag', Icons['displayWindow']['pan'],
+                                      self.parent.OnDrag),
+                                     ('zoom', Icons['displayWindow']['zoomIn'],
+                                      self.parent.OnZoom),
+                                     ('unzoom', Icons['displayWindow']['zoomBack'],
+                                      self.parent.OnRedraw),
+                                     (None, ),
+                                     ('image', Icons['displayWindow']["saveFile"],
+                                      self.parent.SaveToFile),
+                                     ('print', Icons['displayWindow']["print"],
+                                      self.parent.PrintMenu),
+                                     (None, ),
+                                     ('settings', icons["options"],
+                                      self.parent.PlotOptionsMenu),
+                                     ('quit', icons["quit"],
+                                      self.parent.OnQuit),
+                                     ))
+
 class LMWorkspaceToolbar(AbstractToolbar):
 class LMWorkspaceToolbar(AbstractToolbar):
     """!Layer Manager `workspace` toolbar
     """!Layer Manager `workspace` toolbar
     """
     """

+ 273 - 26
gui/wxpython/gui_modules/wxplot.py

@@ -28,6 +28,7 @@ import gcmd
 from render      import Map
 from render      import Map
 from toolbars    import Histogram2Toolbar
 from toolbars    import Histogram2Toolbar
 from toolbars    import ProfileToolbar
 from toolbars    import ProfileToolbar
+from toolbars    import ScatterplotToolbar
 from preferences import globalSettings as UserSettings
 from preferences import globalSettings as UserSettings
 
 
 import wxplot_dialogs as dialogs
 import wxplot_dialogs as dialogs
@@ -48,10 +49,10 @@ except ImportError:
 
 
 class AbstractPlotFrame(wx.Frame):
 class AbstractPlotFrame(wx.Frame):
     """!Abstract PyPlot display frame class"""
     """!Abstract PyPlot display frame class"""
-    def __init__(self, parent, id = wx.ID_ANY, title = '', size = (700, 400),
+    def __init__(self, parent = None, id = wx.ID_ANY, size = (700, 300),
                  style = wx.DEFAULT_FRAME_STYLE, rasterList = [],  **kwargs):
                  style = wx.DEFAULT_FRAME_STYLE, rasterList = [],  **kwargs):
 
 
-        wx.Frame.__init__(self, parent, id, title, size = size, style = style, **kwargs)
+        wx.Frame.__init__(self, parent, id, size = size, style = style, **kwargs)
         
         
         self.parent = parent            # MapFrame
         self.parent = parent            # MapFrame
         self.mapwin = self.parent.MapWindow
         self.mapwin = self.parent.MapWindow
@@ -60,7 +61,7 @@ class AbstractPlotFrame(wx.Frame):
         self.raster = {}    # dictionary of raster maps and their plotting parameters
         self.raster = {}    # dictionary of raster maps and their plotting parameters
         self.plottype = ''
         self.plottype = ''
         
         
-        self.pstyledict = { 'solid' : wx.SOLID,
+        self.linestyledict = { 'solid' : wx.SOLID,
                             'dot' : wx.DOT,
                             'dot' : wx.DOT,
                             'long-dash' : wx.LONG_DASH,
                             'long-dash' : wx.LONG_DASH,
                             'short-dash' : wx.SHORT_DASH,
                             'short-dash' : wx.SHORT_DASH,
@@ -136,7 +137,7 @@ class AbstractPlotFrame(wx.Frame):
                                                     wx.FONTSTYLE_NORMAL,
                                                     wx.FONTSTYLE_NORMAL,
                                                     wx.FONTWEIGHT_NORMAL)
                                                     wx.FONTWEIGHT_NORMAL)
         
         
-        if self.plottype != 'histogram':
+        if self.plottype == 'profile':
             self.properties['marker'] = UserSettings.Get(group = self.plottype, key = 'marker')
             self.properties['marker'] = UserSettings.Get(group = self.plottype, key = 'marker')
             # changing color string to tuple for markers/points
             # changing color string to tuple for markers/points
             colstr = str(self.properties['marker']['color'])
             colstr = str(self.properties['marker']['color'])
@@ -172,7 +173,7 @@ class AbstractPlotFrame(wx.Frame):
         else:
         else:
             self.client.SetYSpec('auto')
             self.client.SetYSpec('auto')
         
         
-    def InitRasterOpts(self, rasterList):
+    def InitRasterOpts(self, rasterList, plottype):
         """!Initialize or update raster dictionary for plotting
         """!Initialize or update raster dictionary for plotting
         """
         """
 
 
@@ -186,8 +187,8 @@ class AbstractPlotFrame(wx.Frame):
             except:
             except:
                 continue
                 continue
                 # if r.info cannot parse map, skip it
                 # if r.info cannot parse map, skip it
-
-#            self.raster[r] = UserSettings.Get(group = 'plot', key = 'raster') # some default settings
+                
+            self.raster[r] = UserSettings.Get(group = plottype, key = 'raster') # some default settings
             rdict[r] = {} # initialize sub-dictionaries for each raster in the list
             rdict[r] = {} # initialize sub-dictionaries for each raster in the list
 
 
             if ret['units'] == '(none)' or ret['units'] == '' or ret['units'] == None:
             if ret['units'] == '(none)' or ret['units'] == '' or ret['units'] == None:
@@ -213,6 +214,58 @@ class AbstractPlotFrame(wx.Frame):
             
             
         return rdict
         return rdict
             
             
+    def InitRasterPairs(self, rasterList, plottype):
+        """!Initialize or update raster dictionary with raster pairs for
+            bivariate scatterplots
+        """
+        
+        if len(rasterList) == 0: return
+
+        rdict = {} # initialize a dictionary
+        for rpair in rasterList:
+            idx = rasterList.index(rpair)
+            
+            try:
+                ret0 = raster.raster_info(rpair[0])
+                ret1 = raster.raster_info(rpair[1])
+
+            except:
+                continue
+                # if r.info cannot parse map, skip it
+
+            self.raster[rpair] = UserSettings.Get(group = plottype, key = 'rasters') # some default settings
+            rdict[rpair] = {} # initialize sub-dictionaries for each raster in the list
+            rdict[rpair][0] = {}
+            rdict[rpair][1] = {}
+
+            if ret0['units'] == '(none)' or ret['units'] == '' or ret['units'] == None:
+                rdict[rpair][0]['units'] = ''
+            else:
+                self.raster[rpair][0]['units'] = ret0['units']
+
+            if ret1['units'] == '(none)' or ret['units'] == '' or ret['units'] == None:
+                rdict[rpair][1]['units'] = ''
+            else:
+                self.raster[rpair][1]['units'] = ret1['units']
+
+            rdict[rpair]['plegend'] = rpair[0].split('@')[0] + ' vs ' + rpair[1].split('@')[0]
+            rdict[rpair]['datalist'] = [] # list of cell value,frequency pairs for plotting histogram
+            rdict[rpair]['ptype'] = 'dot'
+            rdict[rpair][0]['datatype'] = ret0['datatype']
+            rdict[rpair][1]['datatype'] = ret1['datatype']
+            rdict[rpair]['psize'] = 1
+            rdict[rpair]['pfill'] = 'solid'
+            
+            if idx <= len(self.colorList):
+                rdict[rpair]['pcolor'] = self.colorDict[self.colorList[idx]]
+            else:
+                r = randint(0, 255)
+                b = randint(0, 255)
+                g = randint(0, 255)
+                rdict[rpair]['pcolor'] = ((r,g,b,255))
+            
+        return rdict
+
     def SetGraphStyle(self):
     def SetGraphStyle(self):
         """!Set plot and text options
         """!Set plot and text options
         """
         """
@@ -234,7 +287,7 @@ class AbstractPlotFrame(wx.Frame):
         if self.properties['y-axis']['prop']['type'] == 'custom':
         if self.properties['y-axis']['prop']['type'] == 'custom':
             self.client.SetYSpec('min')
             self.client.SetYSpec('min')
         else:
         else:
-            self.client.SetYSpec(self.properties['y-axis']['prop']['type'])
+            self.client.SetYSpec(self.properties['y-axis']['prop'])
 
 
         if self.properties['x-axis']['prop']['type'] == 'custom' and \
         if self.properties['x-axis']['prop']['type'] == 'custom' and \
                self.properties['x-axis']['prop']['min'] < self.properties['x-axis']['prop']['max']:
                self.properties['x-axis']['prop']['min'] < self.properties['x-axis']['prop']['max']:
@@ -273,7 +326,6 @@ class AbstractPlotFrame(wx.Frame):
     def DrawPlot(self, plotlist):
     def DrawPlot(self, plotlist):
         """!Draw line and point plot from list plot elements.
         """!Draw line and point plot from list plot elements.
         """
         """
-
         self.plot = plot.PlotGraphics(plotlist,
         self.plot = plot.PlotGraphics(plotlist,
                                          self.ptitle,
                                          self.ptitle,
                                          self.xlabel,
                                          self.xlabel,
@@ -497,15 +549,14 @@ class AbstractPlotFrame(wx.Frame):
         self.Destroy()
         self.Destroy()
         
         
 class HistFrame(AbstractPlotFrame):
 class HistFrame(AbstractPlotFrame):
-    def __init__(self, parent, title = _("GRASS Histogramming Tool"),
-                 rasterList = [], **kwargs):
+    def __init__(self, parent, id, pos, style, size, rasterList = []):
         """!Mainframe for displaying histogram of raster map. Uses wx.lib.plot.
         """!Mainframe for displaying histogram of raster map. Uses wx.lib.plot.
         """
         """
-        AbstractPlotFrame.__init__(self, parent, title = title,
-                                   rasterList = rasterList, **kwargs)
+        AbstractPlotFrame.__init__(self, parent, rasterList = rasterList, **kwargs)
         
         
         self.toolbar = Histogram2Toolbar(parent = self)
         self.toolbar = Histogram2Toolbar(parent = self)
         self.SetToolBar(self.toolbar)
         self.SetToolBar(self.toolbar)
+        self.SetLabel(_("GRASS Histogramming Tool"))
 
 
         #
         #
         # Init variables
         # Init variables
@@ -524,7 +575,7 @@ class HistFrame(AbstractPlotFrame):
                     "indigo"]
                     "indigo"]
         
         
         if len(self.rasterList) > 0: # set raster name(s) from layer manager if a map is selected
         if len(self.rasterList) > 0: # set raster name(s) from layer manager if a map is selected
-            self.InitRasterOpts(self.rasterList)
+            self.InitRasterOpts(self.rasterList, self.plottype)
 
 
         self._initOpts()
         self._initOpts()
 
 
@@ -555,14 +606,11 @@ class HistFrame(AbstractPlotFrame):
             self.bins = dlg.bins
             self.bins = dlg.bins
             self.histtype = dlg.histtype
             self.histtype = dlg.histtype
             self.maptype = dlg.maptype
             self.maptype = dlg.maptype
-            self.raster = self.InitRasterOpts(self.rasterList)
+            self.raster = self.InitRasterOpts(self.rasterList, self.plottype)
 
 
             # plot histogram
             # plot histogram
             if len(self.rasterList) > 0:
             if len(self.rasterList) > 0:
                 self.OnCreateHist(event = None)
                 self.OnCreateHist(event = None)
-                self.SetupHistogram()
-                p = self.CreatePlotList()
-                self.DrawPlot(p)
 
 
         dlg.Destroy()
         dlg.Destroy()
 
 
@@ -658,7 +706,7 @@ class HistFrame(AbstractPlotFrame):
                 self.raster[r]['pline'] = plot.PolyLine(self.raster[r]['datalist'],
                 self.raster[r]['pline'] = plot.PolyLine(self.raster[r]['datalist'],
                                                         colour = col,
                                                         colour = col,
                                                         width = self.raster[r]['pwidth'],
                                                         width = self.raster[r]['pwidth'],
-                                                        style = self.pstyledict[self.raster[r]['pstyle']],
+                                                        style = self.linestyledict[self.raster[r]['pstyle']],
                                                         legend = self.raster[r]['plegend'])
                                                         legend = self.raster[r]['plegend'])
 
 
                 self.plotlist.append(self.raster[r]['pline'])
                 self.plotlist.append(self.raster[r]['pline'])
@@ -679,14 +727,13 @@ class HistFrame(AbstractPlotFrame):
 class ProfileFrame(AbstractPlotFrame):
 class ProfileFrame(AbstractPlotFrame):
     """!Mainframe for displaying profile of one or more raster maps. Uses wx.lib.plot.
     """!Mainframe for displaying profile of one or more raster maps. Uses wx.lib.plot.
     """
     """
-    def __init__(self, parent, title = _("GRASS Profile Analysis Tool"),
-                 rasterList = [], **kwargs):
+    def __init__(self, parent, id, pos, style, size, rasterList = []):
 
 
-        AbstractPlotFrame.__init__(self, parent, title = title,
-                                   rasterList = rasterList, **kwargs)
+        AbstractPlotFrame.__init__(self, parent, rasterList = rasterList, **kwargs)
 
 
         self.toolbar = ProfileToolbar(parent = self)
         self.toolbar = ProfileToolbar(parent = self)
         self.SetToolBar(self.toolbar)
         self.SetToolBar(self.toolbar)
+        self.SetLabel(_("GRASS Profile Analysis Tool"))
 
 
         #
         #
         # Init variables
         # Init variables
@@ -705,7 +752,7 @@ class ProfileFrame(AbstractPlotFrame):
                     "indigo"]
                     "indigo"]
 
 
         if len(self.rasterList) > 0: # set raster name(s) from layer manager if a map is selected
         if len(self.rasterList) > 0: # set raster name(s) from layer manager if a map is selected
-            self.InitRasterOpts(self.rasterList)
+            self.InitRasterOpts(self.rasterList, self.plottype)
             
             
         
         
         self._initOpts()
         self._initOpts()
@@ -746,7 +793,7 @@ class ProfileFrame(AbstractPlotFrame):
 
 
         if dlg.ShowModal() == wx.ID_OK:
         if dlg.ShowModal() == wx.ID_OK:
             self.rasterList = dlg.rasterList
             self.rasterList = dlg.rasterList
-            self.raster = self.InitRasterOpts(self.rasterList)
+            self.raster = self.InitRasterOpts(self.rasterList, self.plottype)
             
             
             # plot profile
             # plot profile
             if len(self.mapwin.polycoords) > 0 and len(self.rasterList) > 0:
             if len(self.mapwin.polycoords) > 0 and len(self.rasterList) > 0:
@@ -934,7 +981,7 @@ class ProfileFrame(AbstractPlotFrame):
             self.raster[r]['pline'] = plot.PolyLine(self.raster[r]['datalist'],
             self.raster[r]['pline'] = plot.PolyLine(self.raster[r]['datalist'],
                                                     colour = col,
                                                     colour = col,
                                                     width = self.raster[r]['pwidth'],
                                                     width = self.raster[r]['pwidth'],
-                                                    style = self.pstyledict[self.raster[r]['pstyle']],
+                                                    style = self.linestyledict[self.raster[r]['pstyle']],
                                                     legend = self.raster[r]['plegend'])
                                                     legend = self.raster[r]['plegend'])
 
 
             self.plotlist.append(self.raster[r]['pline'])
             self.plotlist.append(self.raster[r]['pline'])
@@ -978,3 +1025,203 @@ class ProfileFrame(AbstractPlotFrame):
                 file.close()
                 file.close()
 
 
         dlg.Destroy()
         dlg.Destroy()
+        
+class ScatterFrame(AbstractPlotFrame):
+    """!Mainframe for displaying bivariate scatter plot of two raster maps. Uses wx.lib.plot.
+    """
+    def __init__(self, parent, id, pos, style, size, rasterList = []):
+
+        AbstractPlotFrame.__init__(self, parent)
+        
+        self.toolbar = ScatterplotToolbar(parent = self)
+        self.SetToolBar(self.toolbar)
+        self.SetLabel(_("GRASS Bivariate Scatterplot Tool"))
+
+        #
+        # Init variables
+        #
+        self.rasterList = rasterList
+        self.plottype = 'scatter'
+        self.ptitle = _('Bivariate Scatterplot')     # title of window
+        self.xlabel = _("Raster cell values")           # default X-axis label
+        self.ylabel = _("Raster cell values")           # default Y-axis label
+        self.maptype = 'raster'                         # default type of scatterplot
+        self.scattertype = 'normal' 
+        self.bins = 255
+        self.colorList = ["blue", "red", "black", "green", "yellow", "magenta", "cyan", \
+                    "aqua", "grey", "orange", "brown", "purple", "violet", \
+                    "indigo"]
+        
+        if len(self.rasterList) > 0: # set raster name(s) from layer manager if a map is selected
+            self.InitRasterOpts(self.rasterList, 'scatter')
+
+        self._initOpts()
+
+    def _initOpts(self):
+        """!Initialize plot options
+        """
+        self.InitPlotOpts('scatter')            
+
+    def OnCreateScatter(self, event):
+        """!Main routine for creating a scatterplot. Uses r.stats to
+        create a list of cell value pairs. This is passed to
+        plot to create a scatterplot.
+        """
+        self.SetCursor(self.parent.cursors["default"])
+        self.SetGraphStyle()
+        self.SetupScatterplot()
+        p = self.CreatePlotList()
+        self.DrawPlot(p)
+
+    def OnSelectRaster(self, event):
+        """!Select raster map(s) to profile
+        """
+        dlg = dialogs.ScatterRasterDialog(parent = self)
+
+        if dlg.ShowModal() == wx.ID_OK:
+            rlist = dlg.rasterList
+            if rlist < 2: return                        # need at least 2 rasters for scatterplot
+            self.bins = dlg.bins                        # bins for r.stats with float and dcell maps
+            self.scattertype = dlg.scattertype          # scatterplot or bubbleplot
+            self.rasterList = self.CreatePairs(rlist)   # list of raster pairs (tuples)
+            self.raster = self.InitRasterPairs(self.rasterList, 'scatter') # dictionary of raster pairs
+
+            # plot histogram
+            if len(self.rasterList) > 0:
+                self.OnCreateScatter(event = None)
+
+        dlg.Destroy()
+        
+    def CreatePairs(self, rlist):
+        """!Transforms list of rasters into tuples of raster pairs
+        """
+        rasterList = []
+        next = 'first'
+        for r in rlist:
+            if next == 'first':
+                first = r
+                next = 'second'
+            else:
+                second = r
+                t = (first, second)
+                rasterList.append(t)
+                next = 'first'
+                first = second = ''
+                
+        return rasterList
+
+    def SetupScatterplot(self):
+        """!Build data list for ploting each raster
+        """
+
+        #
+        # initialize title string
+        #
+        self.ptitle = _('Bivariate Scatterplot of ')        
+
+        #
+        # create a datalist for plotting for each raster pair
+        #
+        if len(self.rasterList) == 0: return  # at least 1 pair of maps needed to plot        
+        
+        for rpair in self.rasterList:
+            self.raster[rpair]['datalist'] = self.CreateDatalist(rpair)
+            
+            # update title
+            self.ptitle += '%s vs %s, ' % (rpair[0].split('@')[0], rpair[1].split('@')[0])
+
+        self.ptitle = self.ptitle.strip(', ')
+        
+        #
+        # set xlabel & ylabel based on raster maps of first pair to be plotted
+        #
+        units = self.raster[self.rasterList[0]][0]['units']
+        if units != '' and units != '(none)' and units != None:
+            self.xlabel = _('Raster cell values %s') % units
+        else:
+            self.xlabel = _('Raster cell values') 
+
+        units = self.raster[self.rasterList[0]][1]['units']
+        if units != '' and units != '(none)' and units != None:
+            self.ylabel = _('Raster cell values %s') % units
+        else:
+            self.ylabel = _('Raster cell values') 
+
+    def CreateDatalist(self, rpair):
+        """!Build a list of cell value, frequency pairs for histogram
+            frequency can be in cell counts, percents, or area
+        """
+        datalist = []
+        
+        if self.scattertype == 'bubble': 
+            freqflag = 'cn'
+        else:
+            freqflag = 'n'
+                
+        try:
+            ret = gcmd.RunCommand("r.stats",
+                                  parent = self,
+                                  input = '%s,%s' % rpair,
+                                  flags = freqflag,
+                                  nsteps = self.bins,
+                                  fs = ',',
+                                  quiet = True,
+                                  read = True)
+            
+            
+            if not ret:
+                return datalist
+            
+            for line in ret.splitlines():
+                rast1, rast2 = line.strip().split(',')
+                rast1 = rast1.strip()
+                if '-' in rast1: rast1 = rast1.split('-')[0]
+                rast2 = rast2.strip()
+                if '-' in rast2: rast2 = rast2.split('-')[0]
+                
+                rast1 = rast1.encode('ascii', 'ignore')
+                rast2 = rast2.encode('ascii', 'ignore')
+                    
+                datalist.append((rast1,rast2))
+
+            return datalist
+        except gcmd.GException, e:
+            gcmd.GError(parent = self,
+                        message = e.value)
+            return None
+        
+    def CreatePlotList(self):
+        """!Make list of elements to plot
+        """
+        # graph the cell value, frequency pairs for the histogram
+        self.plotlist = []
+
+        for rpair in self.rasterList:
+            if 'datalist' not in self.raster[rpair] or \
+                self.raster[rpair]['datalist'] == None: return
+            
+            if len(self.raster[rpair]['datalist']) > 0:
+                col = wx.Color(self.raster[rpair]['pcolor'][0],
+                               self.raster[rpair]['pcolor'][1],
+                               self.raster[rpair]['pcolor'][2],
+                               255)
+                scatterpoints = plot.PolyMarker(self.raster[rpair]['datalist'],
+                                                legend = ' ' + self.raster[rpair]['plegend'],
+                                                colour = col,size = self.raster[rpair]['psize'],
+                                                fillstyle = self.ptfilldict[self.raster[rpair]['pfill']],
+                                                marker = self.raster[rpair]['ptype'])
+
+                self.plotlist.append(scatterpoints)
+          
+        if len(self.plotlist) > 0:        
+            return self.plotlist
+        else:
+            return None
+
+    def Update(self):
+        """!Update histogram after changing options
+        """
+        self.SetGraphStyle()
+        p = self.CreatePlotList()
+        self.DrawPlot(p)
+        

+ 277 - 60
gui/wxpython/gui_modules/wxplot_dialogs.py

@@ -20,6 +20,7 @@ This program is free software under the GNU General Public License
 
 
 import os
 import os
 import sys
 import sys
+from types import *
 
 
 import wx
 import wx
 import wx.lib.colourselect as  csel
 import wx.lib.colourselect as  csel
@@ -33,7 +34,7 @@ from grass.script import core   as grass
 
 
 class ProfileRasterDialog(wx.Dialog):
 class ProfileRasterDialog(wx.Dialog):
     def __init__(self, parent, id = wx.ID_ANY, 
     def __init__(self, parent, id = wx.ID_ANY, 
-                 title = _("Select raster map or imagery group to histogram"),
+                 title = _("Select raster maps to profile"),
                  style = wx.DEFAULT_DIALOG_STYLE, **kwargs):
                  style = wx.DEFAULT_DIALOG_STYLE, **kwargs):
         """!Dialog to select raster maps to profile.
         """!Dialog to select raster maps to profile.
         """
         """
@@ -106,6 +107,119 @@ class ProfileRasterDialog(wx.Dialog):
         for idx in range(0,n):
         for idx in range(0,n):
             self.rasterList.append(rList[idx])
             self.rasterList.append(rList[idx])
 
 
+class ScatterRasterDialog(wx.Dialog):
+    def __init__(self, parent, id = wx.ID_ANY, 
+                 title = _("Select pairs of raster maps for scatterplots"),
+                 style = wx.DEFAULT_DIALOG_STYLE, **kwargs):
+        """!Dialog to select raster maps to profile.
+        """
+
+        wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs)
+
+        self.parent = parent
+        self.rasterList = self.parent.rasterList
+        self.bins = self.parent.bins
+        self.scattertype = self.parent.scattertype
+        self.maptype = self.parent.maptype
+        self.spinbins = ''        
+        self.colorList = ["blue", "red", "green", "yellow", "magenta", "cyan", \
+                    "aqua", "black", "grey", "orange", "brown", "purple", "violet", \
+                    "indigo"]
+        
+        self._do_layout()
+        
+    def _do_layout(self):
+
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        box = wx.GridBagSizer (hgap = 3, vgap = 3)
+        
+        # parse raster pair tuples 
+        rastText = ''
+        if len(self.rasterList) > 0:
+            for r in self.rasterList:
+                rastText += '%s,%s,' % r
+            rastText = rastText.rstrip(',')
+        
+        # select rasters
+        txt = _("Select pairs of raster maps for bivariate scatterplots:")
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = txt)
+        box.Add(item = label,
+                flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
+        
+        selection = Select(self, id = wx.ID_ANY,
+                           size = globalvar.DIALOG_GSELECT_SIZE,
+                           type = 'cell', multiple=True)
+        selection.SetValue(rastText)
+        selection.Bind(wx.EVT_TEXT, self.OnSelection)
+        
+        box.Add(item = selection, pos = (0, 1))
+            
+        # Nsteps for FP maps 
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, 
+                              label = _("Number of bins (for FP maps)"))
+        box.Add(item = label,
+                flag = wx.ALIGN_CENTER_VERTICAL, pos = (1, 0))
+        self.spinbins = wx.SpinCtrl(parent = self, id = wx.ID_ANY, value = "", pos = (30, 50),
+                                      size = (100,-1), style = wx.SP_ARROW_KEYS)
+        self.spinbins.SetRange(1,1000)
+        self.spinbins.SetValue(self.bins)
+        box.Add(item = self.spinbins,
+                flag = wx.ALIGN_CENTER_VERTICAL, pos = (1, 1))
+
+#### TODO possibly make bubble plots with marker size proportional to cell counts
+#        # scatterplot type 
+#        label = wx.StaticText(parent = self, id = wx.ID_ANY, 
+#                              label = _("Scatterplot type"))
+#        box.Add(item = label,
+#                flag = wx.ALIGN_CENTER_VERTICAL, pos = (2, 0))
+#        types = ['normal', 'bubble']
+#        scattertype = wx.ComboBox(parent = self, id = wx.ID_ANY, size = (250, -1),
+#                                choices = types, style = wx.CB_DROPDOWN)
+#        scattertype.SetStringSelection(self.scattertype)
+#        box.Add(item = scattertype,
+#                flag = wx.ALIGN_CENTER_VERTICAL, pos = (2, 1))
+          
+        sizer.Add(item = box, proportion = 0,
+                  flag = wx.ALL, border = 10)
+
+        line = wx.StaticLine(parent = self, id = wx.ID_ANY, size = (20, -1), style = wx.LI_HORIZONTAL)
+        sizer.Add(item = line, proportion = 0,
+                  flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, border = 5)
+
+        btnsizer = wx.StdDialogButtonSizer()
+
+        btn = wx.Button(self, wx.ID_OK)
+        btn.SetDefault()
+        btnsizer.AddButton(btn)
+
+        btn = wx.Button(self, wx.ID_CANCEL)
+        btnsizer.AddButton(btn)
+        btnsizer.Realize()
+
+        sizer.Add(item = btnsizer, proportion = 0, flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+
+        self.spinbins.Bind(wx.EVT_TEXT, self.OnSetBins)
+        self.spinbins.Bind(wx.EVT_SPINCTRL, self.OnSetBins)
+#        scattertype.Bind(wx.EVT_TEXT, self.OnSetScattertypes)
+
+        self.SetSizer(sizer)
+        sizer.Fit(self)
+
+    def OnSelection(self, event):
+        """!Select raster maps for scatterplot. Must select maps in pairs.
+        """
+        self.rasterList = []
+        self.rasterList = event.GetString().split(',')
+            
+    def OnSetBins(self, event):
+        """!Bins for histogramming FP maps (=nsteps in r.stats)
+        """
+        self.bins = self.spinbins.GetValue()
+        
+    def OnSetScattertypes(self, event):
+        self.scattertype = event.GetString()
+
 class HistRasterDialog(wx.Dialog):
 class HistRasterDialog(wx.Dialog):
     def __init__(self, parent, id = wx.ID_ANY, 
     def __init__(self, parent, id = wx.ID_ANY, 
                  title = _("Select raster map or imagery group to histogram"),
                  title = _("Select raster map or imagery group to histogram"),
@@ -538,7 +652,7 @@ class OptDialog(wx.Dialog):
         wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs)
         wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs)
         # init variables
         # init variables
         self.parent = parent
         self.parent = parent
-        self.pstyledict = parent.pstyledict
+        self.linestyledict = parent.linestyledict
         self.ptfilldict = parent.ptfilldict
         self.ptfilldict = parent.ptfilldict
         self.plottype = plottype
         self.plottype = plottype
         
         
@@ -571,6 +685,17 @@ class OptDialog(wx.Dialog):
                               caption = _("Warning"), style = wx.OK | wx.ICON_ERROR)
                               caption = _("Warning"), style = wx.OK | wx.ICON_ERROR)
             
             
         self._do_layout()
         self._do_layout()
+        
+    def ConvertTuples(self, tlist):
+        """!Converts tuples to strings when rasterList contains raster pairs
+            for scatterplot
+        """
+        list = []
+        for i in tlist:
+            i = str(i).strip('()')
+            list.append(i)
+            
+        return list
 
 
     def _do_layout(self):
     def _do_layout(self):
         """!Do layout"""
         """!Do layout"""
@@ -584,73 +709,86 @@ class OptDialog(wx.Dialog):
         self.wxId['pcolor']  = 0
         self.wxId['pcolor']  = 0
         self.wxId['pwidth']  = 0
         self.wxId['pwidth']  = 0
         self.wxId['pstyle']  = 0
         self.wxId['pstyle']  = 0
+        self.wxId['psize'] = 0
+        self.wxId['ptype'] = 0
+        self.wxId['pfill'] = 0
         self.wxId['plegend'] = 0
         self.wxId['plegend'] = 0
         self.wxId['marker'] = {}
         self.wxId['marker'] = {}
         self.wxId['x-axis'] = {}
         self.wxId['x-axis'] = {}
         self.wxId['y-axis'] = {}
         self.wxId['y-axis'] = {}
-
+        
         #
         #
-        # plot line settings
+        # plot line settings and point settings
         #
         #
-        if len(self.rasterList) > 0:
-            box = wx.StaticBox(parent = self, id = wx.ID_ANY,
-                               label = _("Map/image plotted"))
-            boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-            
-            gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
-            
-            row = 0
-            self.mapchoice = wx.Choice(parent = self, id = wx.ID_ANY, size = (300, -1),
-                                       choices = self.rasterList)
-            if not self.map:
-                self.map = self.rasterList[self.mapchoice.GetCurrentSelection()]
-            else:
-                self.mapchoice.SetStringSelection(self.map)
-            gridSizer.Add(item = self.mapchoice, flag = wx.ALIGN_CENTER_VERTICAL, 
-                          pos = (row, 0), span = (1, 2))
+        if len(self.rasterList) == 0: return
+        
+        box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                           label = _("Map/image plotted"))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        
+        row = 0
+        choicelist = []
+        for i in self.rasterList:
+            choicelist.append(str(i))
+
+        self.mapchoice = wx.Choice(parent = self, id = wx.ID_ANY, size = (300, -1),
+                                   choices = choicelist)
+        if not self.map:
+            self.map = self.rasterList[self.mapchoice.GetCurrentSelection()]
+        else:
+            self.mapchoice.SetStringSelection(str(self.map))
             
             
+                
+        gridSizer.Add(item = self.mapchoice, flag = wx.ALIGN_CENTER_VERTICAL, 
+                      pos = (row, 0), span = (1, 2))
+        
+        #
+        # options for line plots (profiles and histograms)
+        #
+        if self.plottype != 'scatter':
             row +=1            
             row +=1            
             label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Line color"))
             label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Line color"))
             gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
             gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
-            pcolor = csel.ColourSelect(parent = self, id = wx.ID_ANY, colour = self.raster[self.map]['pcolor'])
-            self.wxId['pcolor'] = pcolor.GetId()
-            gridSizer.Add(item = pcolor, pos = (row, 1))
+            color = csel.ColourSelect(parent = self, id = wx.ID_ANY, colour = self.raster[self.map]['pcolor'])
+            self.wxId['pcolor'] = color.GetId()
+            gridSizer.Add(item = color, pos = (row, 1))
 
 
             row += 1
             row += 1
             label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Line width"))
             label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Line width"))
             gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
             gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
-            pwidth = wx.SpinCtrl(parent = self, id = wx.ID_ANY, value = "",
+            width = wx.SpinCtrl(parent = self, id = wx.ID_ANY, value = "",
                                  size = (50,-1), style = wx.SP_ARROW_KEYS)
                                  size = (50,-1), style = wx.SP_ARROW_KEYS)
-            pwidth.SetRange(1, 10)
-            pwidth.SetValue(self.raster[self.map]['pwidth'])
-            self.wxId['pwidth'] = pwidth.GetId()
-            gridSizer.Add(item = pwidth, pos = (row, 1))
+            width.SetRange(1, 10)
+            width.SetValue(self.raster[self.map]['pwidth'])
+            self.wxId['pwidth'] = width.GetId()
+            gridSizer.Add(item = width, pos = (row, 1))
 
 
             row +=1
             row +=1
             label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Line style"))
             label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Line style"))
             gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
             gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
-            pstyle = wx.Choice(parent = self, id = wx.ID_ANY, 
-                                 size = (120, -1), choices = self.pstyledict.keys(), style = wx.CB_DROPDOWN)
-            pstyle.SetStringSelection(self.raster[self.map]['pstyle'])
-            self.wxId['pstyle'] = pstyle.GetId()
-            gridSizer.Add(item = pstyle, pos = (row, 1))
-
-            row += 1
-            label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Legend"))
-            gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
-            plegend = wx.TextCtrl(parent = self, id = wx.ID_ANY, value = "", size = (200,-1))
-            plegend.SetValue(self.raster[self.map]['plegend'])
-            gridSizer.Add(item = plegend, pos = (row, 1))
-            self.wxId['plegend'] = plegend.GetId()
-            
-            boxSizer.Add(item = gridSizer)
-            boxMainSizer.Add(item = boxSizer, flag = wx.ALL, border = 3)
+            style = wx.Choice(parent = self, id = wx.ID_ANY, 
+                                 size = (120, -1), choices = self.linestyledict.keys(), style = wx.CB_DROPDOWN)
+            style.SetStringSelection(self.raster[self.map]['pstyle'])
+            self.wxId['pstyle'] = style.GetId()
+            gridSizer.Add(item = style, pos = (row, 1))
 
 
+        row += 1
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Legend"))
+        gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
+        legend = wx.TextCtrl(parent = self, id = wx.ID_ANY, value = "", size = (200,-1))
+        legend.SetValue(self.raster[self.map]['plegend'])
+        gridSizer.Add(item = legend, pos = (row, 1))
+        self.wxId['plegend'] = legend.GetId()
+        
+        boxSizer.Add(item = gridSizer)
+        boxMainSizer.Add(item = boxSizer, flag = wx.ALL, border = 3)
 
 
         #
         #
-        # segment marker settings
+        # segment marker settings for profiles only
         #       
         #       
-        if self.plottype != 'histogram':
+        if self.plottype == 'profile':
             box = wx.StaticBox(parent = self, id = wx.ID_ANY,
             box = wx.StaticBox(parent = self, id = wx.ID_ANY,
                                label = " %s " % _("Transect segment marker settings"))
                                label = " %s " % _("Transect segment marker settings"))
             
             
@@ -698,10 +836,61 @@ class OptDialog(wx.Dialog):
             boxSizer.Add(item = gridSizer)
             boxSizer.Add(item = gridSizer)
             boxMainSizer.Add(item = boxSizer, flag = wx.ALL, border = 3)
             boxMainSizer.Add(item = boxSizer, flag = wx.ALL, border = 3)
             
             
+        #
+        # point options for scatterplots
+        #
+        elif self.plottype == 'scatter':
+            box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                               label = " %s " % _("Scatterplot points"))
+            
+            boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+            
+            gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+            label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Color"))
+            gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
+            ptcolor = csel.ColourSelect(parent = self, id = wx.ID_ANY, colour = self.raster[self.map]['pcolor'])
+            self.wxId['pcolor'] = ptcolor.GetId()
+            gridSizer.Add(item = ptcolor, pos = (0, 1))
+
+            label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Size"))
+            gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (1, 0))
+            ptsize = wx.SpinCtrl(parent = self, id = wx.ID_ANY, value = "",
+                                 size = (50, -1), style = wx.SP_ARROW_KEYS)
+            ptsize.SetRange(1, 10)
+            ptsize.SetValue(self.raster[self.map]['psize'])
+            self.wxId['psize'] = ptsize.GetId()
+            gridSizer.Add(item = ptsize, pos = (1, 1))
+            
+            label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Style"))
+            gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (2, 0))
+            ptfill = wx.ComboBox(parent = self, id = wx.ID_ANY,
+                                 size = (120, -1), choices = self.ptfilldict.keys(), style = wx.CB_DROPDOWN)
+            ptfill.SetStringSelection(self.raster[self.map]['pfill'])
+            self.wxId['pfill'] = ptfill.GetId()
+            gridSizer.Add(item = ptfill, pos = (2, 1))
+            
+            label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Legend"))
+            gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (3, 0))
+            ptlegend = wx.TextCtrl(parent = self, id = wx.ID_ANY, value = "", size = (200,-1))
+            ptlegend.SetValue(self.raster[self.map]['plegend'])
+            self.wxId['plegend'] = ptlegend.GetId()
+            gridSizer.Add(item = ptlegend, pos = (3, 1))
+                    
+            label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Type"))
+            gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (4, 0))
+            pttype = wx.ComboBox(parent = self, 
+                                 size = (200, -1), choices = self.pttypelist, style = wx.CB_DROPDOWN)
+            pttype.SetStringSelection(self.raster[self.map]['ptype'])
+            self.wxId['ptype'] = pttype.GetId()
+            gridSizer.Add(item = pttype, pos = (4, 1))
+
+            boxSizer.Add(item = gridSizer)
+            boxMainSizer.Add(item = boxSizer, flag = wx.ALL, border = 3)
+            
         sizer.Add(item = boxMainSizer, flag = wx.ALL | wx.EXPAND, border = 3)
         sizer.Add(item = boxMainSizer, flag = wx.ALL | wx.EXPAND, border = 3)
 
 
         #
         #
-        # axis options
+        # axis options for all plots
         #
         #
         box = wx.StaticBox(parent = self, id = wx.ID_ANY,
         box = wx.StaticBox(parent = self, id = wx.ID_ANY,
                            label = " %s " % _("Axis settings"))
                            label = " %s " % _("Axis settings"))
@@ -763,7 +952,7 @@ class OptDialog(wx.Dialog):
         middleSizer.Add(item = boxMainSizer, flag = wx.ALL | wx.EXPAND, border = 3)
         middleSizer.Add(item = boxMainSizer, flag = wx.ALL | wx.EXPAND, border = 3)
 
 
         #
         #
-        # grid & legend options
+        # grid & legend options for all plots
         #
         #
         self.wxId['grid'] = {}
         self.wxId['grid'] = {}
         self.wxId['legend'] = {}
         self.wxId['legend'] = {}
@@ -842,10 +1031,20 @@ class OptDialog(wx.Dialog):
         # bindings for buttons and map plot settings controls
         # bindings for buttons and map plot settings controls
         #
         #
         self.mapchoice.Bind(wx.EVT_CHOICE, self.OnSetMap)
         self.mapchoice.Bind(wx.EVT_CHOICE, self.OnSetMap)
-        pcolor.Bind(csel.EVT_COLOURSELECT, self.OnSetOpt)
-        pwidth.Bind(wx.EVT_SPINCTRL, self.OnSetOpt)
-        pstyle.Bind(wx.EVT_CHOICE, self.OnSetOpt)
-        plegend.Bind(wx.EVT_TEXT, self.OnSetOpt)
+        
+        if self.plottype != 'scatter':
+            color.Bind(csel.EVT_COLOURSELECT, self.OnSetOpt)
+            width.Bind(wx.EVT_SPINCTRL, self.OnSetOpt)
+            style.Bind(wx.EVT_CHOICE, self.OnSetOpt)
+            legend.Bind(wx.EVT_TEXT, self.OnSetOpt)
+            
+        if self.plottype != 'histogram':
+            ptcolor.Bind(csel.EVT_COLOURSELECT, self.OnSetOpt)
+            ptsize.Bind(wx.EVT_SPINCTRL, self.OnSetOpt)
+            ptfill.Bind(wx.EVT_CHOICE, self.OnSetOpt)
+            ptlegend.Bind(wx.EVT_TEXT, self.OnSetOpt)
+            pttype.Bind(wx.EVT_CHOICE, self.OnSetOpt)
+            
         btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
         btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
         btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
         btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
         btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
         btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
@@ -855,13 +1054,24 @@ class OptDialog(wx.Dialog):
 
 
     def OnSetMap(self, event):
     def OnSetMap(self, event):
         """!Handler for changing map selection"""
         """!Handler for changing map selection"""
-        self.map = event.GetString()
+        idx = event.GetSelection()
+        self.map = self.rasterList[idx]
         
         
-        # update plot settings controls for selected map
+        # update settings controls for all plots
         self.FindWindowById(self.wxId['pcolor']).SetColour(self.raster[self.map]['pcolor'])
         self.FindWindowById(self.wxId['pcolor']).SetColour(self.raster[self.map]['pcolor'])
-        self.FindWindowById(self.wxId['pwidth']).SetValue(self.raster[self.map]['pwidth'])
-        self.FindWindowById(self.wxId['pstyle']).SetStringSelection(self.raster[self.map]['pstyle'])
         self.FindWindowById(self.wxId['plegend']).SetValue(self.raster[self.map]['plegend'])
         self.FindWindowById(self.wxId['plegend']).SetValue(self.raster[self.map]['plegend'])
+
+        # update settings controls for histograms and profiles
+        if self.plottype != 'scatter':
+            self.FindWindowById(self.wxId['pwidth']).SetValue(self.raster[self.map]['pwidth'])
+            self.FindWindowById(self.wxId['pstyle']).SetStringSelection(self.raster[self.map]['pstyle'])
+
+        # update settings controls for scatterplots
+        elif self.plottype == 'scatter':
+            self.FindWindowById(self.wxId['psize']).SetValue(self.raster[self.map]['psize'])
+            self.FindWindowById(self.wxId['ptype']).SetStringSelection(self.raster[self.map]['ptype'])
+            self.FindWindowById(self.wxId['pfill']).SetStringSelection(self.raster[self.map]['pfill'])
+            
         self.Refresh()
         self.Refresh()
         
         
     def OnSetOpt(self, event):
     def OnSetOpt(self, event):
@@ -870,7 +1080,7 @@ class OptDialog(wx.Dialog):
         self.UpdateSettings()
         self.UpdateSettings()
         self.parent.SetGraphStyle()
         self.parent.SetGraphStyle()
         if self.parent.plot:
         if self.parent.plot:
-            p = self.parent.CreatPlotList()
+            p = self.parent.CreatePlotList()
             self.parent.DrawPlot(p)
             self.parent.DrawPlot(p)
 
 
     def UpdateSettings(self):
     def UpdateSettings(self):
@@ -878,9 +1088,16 @@ class OptDialog(wx.Dialog):
         
         
         # update plot settings for selected map
         # update plot settings for selected map
         self.raster[self.map]['pcolor'] = self.FindWindowById(self.wxId['pcolor']).GetColour()
         self.raster[self.map]['pcolor'] = self.FindWindowById(self.wxId['pcolor']).GetColour()
-        self.raster[self.map]['pwidth'] = int(self.FindWindowById(self.wxId['pwidth']).GetValue())
-        self.raster[self.map]['pstyle'] = self.FindWindowById(self.wxId['pstyle']).GetStringSelection()
         self.raster[self.map]['plegend'] = self.FindWindowById(self.wxId['plegend']).GetValue()
         self.raster[self.map]['plegend'] = self.FindWindowById(self.wxId['plegend']).GetValue()
+        
+        if self.plottype != 'scatter':
+            self.raster[self.map]['pwidth'] = int(self.FindWindowById(self.wxId['pwidth']).GetValue())
+            self.raster[self.map]['pstyle'] = self.FindWindowById(self.wxId['pstyle']).GetStringSelection()
+            
+        elif self.plottype == 'scatter':
+            self.raster[self.map]['psize'] = self.FindWindowById(self.wxId['psize']).GetValue()
+            self.raster[self.map]['ptype'] = self.FindWindowById(self.wxId['ptype']).GetValue()
+            self.raster[self.map]['pfill'] = self.FindWindowById(self.wxId['pfill']).GetValue()
 
 
         # update settings for entire plot
         # update settings for entire plot
         for axis in ('x-axis', 'y-axis'):
         for axis in ('x-axis', 'y-axis'):
@@ -889,7 +1106,7 @@ class OptDialog(wx.Dialog):
             self.properties[axis]['prop']['max'] = float(self.FindWindowById(self.wxId[axis]['max']).GetValue())
             self.properties[axis]['prop']['max'] = float(self.FindWindowById(self.wxId[axis]['max']).GetValue())
             self.properties[axis]['prop']['log'] = self.FindWindowById(self.wxId[axis]['log']).IsChecked()
             self.properties[axis]['prop']['log'] = self.FindWindowById(self.wxId[axis]['log']).IsChecked()
 
 
-        if self.plottype != 'histogram':
+        if self.plottype == 'profile':
             self.properties['marker']['color'] = self.FindWindowById(self.wxId['marker']['color']).GetColour()
             self.properties['marker']['color'] = self.FindWindowById(self.wxId['marker']['color']).GetColour()
             self.properties['marker']['fill'] = self.FindWindowById(self.wxId['marker']['fill']).GetStringSelection()
             self.properties['marker']['fill'] = self.FindWindowById(self.wxId['marker']['fill']).GetStringSelection()
             self.properties['marker']['size'] = self.FindWindowById(self.wxId['marker']['size']).GetValue()
             self.properties['marker']['size'] = self.FindWindowById(self.wxId['marker']['size']).GetValue()