scatter.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. """!
  2. @package wxplot.scatter
  3. @brief Scatter plotting using PyPlot
  4. Classes:
  5. - scatter::ScatterFrame
  6. - scatter::ScatterToolbar
  7. (C) 2011 by the GRASS Development Team
  8. This program is free software under the GNU General Public License
  9. (>=v2). Read the file COPYING that comes with GRASS for details.
  10. @author Michael Barton, Arizona State University
  11. """
  12. import sys
  13. import wx
  14. import wx.lib.plot as plot
  15. import grass.script as grass
  16. from wxplot.base import BasePlotFrame, PlotIcons
  17. from gui_core.toolbars import BaseToolbar, BaseIcons
  18. from wxplot.dialogs import ScatterRasterDialog, PlotStatsFrame
  19. from core.gcmd import RunCommand, GException, GError, GMessage
  20. from core.utils import _
  21. class ScatterFrame(BasePlotFrame):
  22. """!Mainframe for displaying bivariate scatter plot of two raster maps. Uses wx.lib.plot.
  23. """
  24. def __init__(self, parent, id = wx.ID_ANY, style = wx.DEFAULT_FRAME_STYLE,
  25. size = wx.Size(700, 400),
  26. rasterList = [], **kwargs):
  27. BasePlotFrame.__init__(self, parent, size = size, **kwargs)
  28. self.toolbar = ScatterToolbar(parent = self)
  29. self.SetToolBar(self.toolbar)
  30. self.SetTitle(_("GRASS Bivariate Scatterplot Tool"))
  31. #
  32. # Init variables
  33. #
  34. self.rasterList = rasterList
  35. self.plottype = 'scatter'
  36. self.ptitle = _('Bivariate Scatterplot') # title of window
  37. self.xlabel = _("Raster cell values") # default X-axis label
  38. self.ylabel = _("Raster cell values") # default Y-axis label
  39. self.maptype = 'raster' # default type of scatterplot
  40. self.scattertype = 'normal'
  41. self.bins = 255
  42. self.colorList = ["blue", "red", "black", "green", "yellow", "magenta", "cyan", \
  43. "aqua", "grey", "orange", "brown", "purple", "violet", \
  44. "indigo"]
  45. self._initOpts()
  46. if len(self.rasterList) > 1: # set raster name(s) from layer manager if a map is selected
  47. self.InitRasterOpts(self.rasterList, 'scatter')
  48. else:
  49. self.raster = {}
  50. def _initOpts(self):
  51. """!Initialize plot options
  52. """
  53. self.InitPlotOpts('scatter')
  54. def OnCreateScatter(self, event):
  55. """!Main routine for creating a scatterplot. Uses r.stats to
  56. create a list of cell value pairs. This is passed to
  57. plot to create a scatterplot.
  58. """
  59. self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
  60. self.SetGraphStyle()
  61. self.SetupScatterplot()
  62. p = self.CreatePlotList()
  63. if p:
  64. self.DrawPlot(p)
  65. else:
  66. GMessage(_("Nothing to plot."), parent = self)
  67. def OnSelectRaster(self, event):
  68. """!Select raster map(s) to profile
  69. """
  70. dlg = ScatterRasterDialog(parent = self)
  71. if dlg.ShowModal() == wx.ID_OK:
  72. self.rasterList = dlg.GetRasterPairs()
  73. if not self.rasterList:
  74. GMessage(_("At least 2 raster maps must be specified"), parent = dlg)
  75. return
  76. # scatterplot or bubbleplot
  77. # bins for r.stats with float and dcell maps
  78. self.scattertype, self.bins = dlg.GetSettings()
  79. self.raster = self.InitRasterPairs(self.rasterList, 'scatter') # dictionary of raster pairs
  80. # plot histogram
  81. if self.rasterList:
  82. self.OnCreateScatter(event = None)
  83. dlg.Destroy()
  84. def SetupScatterplot(self):
  85. """!Build data list for ploting each raster
  86. """
  87. #
  88. # initialize title string
  89. #
  90. self.ptitle = _('Bivariate Scatterplot of ')
  91. #
  92. # create a datalist for plotting for each raster pair
  93. #
  94. if len(self.rasterList) == 0: return # at least 1 pair of maps needed to plot
  95. for rpair in self.rasterList:
  96. self.raster[rpair]['datalist'] = self.CreateDatalist(rpair)
  97. # update title
  98. self.ptitle += '%s vs %s, ' % (rpair[0].split('@')[0], rpair[1].split('@')[0])
  99. self.ptitle = self.ptitle.strip(', ')
  100. #
  101. # set xlabel & ylabel based on raster maps of first pair to be plotted
  102. #
  103. self.xlabel = _('Raster cell values')
  104. self.ylabel = _('Raster cell values')
  105. units = self.raster[self.rasterList[0]][0]['units']
  106. if units != '':
  107. self.xlabel += _(': %s') % units
  108. units = self.raster[self.rasterList[0]][1]['units']
  109. if units != '':
  110. self.ylabel += _(': %s') % units
  111. def CreateDatalist(self, rpair):
  112. """!Build a list of cell value, frequency pairs for histogram
  113. frequency can be in cell counts, percents, or area
  114. """
  115. datalist = []
  116. if self.scattertype == 'bubble':
  117. freqflag = 'cn'
  118. else:
  119. freqflag = 'n'
  120. try:
  121. ret = RunCommand("r.stats",
  122. parent = self,
  123. input = '%s,%s' % rpair,
  124. flags = freqflag,
  125. nsteps = self.bins,
  126. sep = ',',
  127. quiet = True,
  128. read = True)
  129. if not ret:
  130. return datalist
  131. for line in ret.splitlines():
  132. rast1, rast2 = line.strip().split(',')
  133. rast1 = rast1.strip()
  134. if '-' in rast1:
  135. if rast1[0] == '-':
  136. rast1 = '-' + rast1.split('-')[1]
  137. else:
  138. rast1 = rast1.split('-')[0]
  139. rast2 = rast2.strip()
  140. if '-' in rast2:
  141. if rast2[0] == '-':
  142. rast2 = '-' + rast2.split('-')[1]
  143. else:
  144. rast2 = rast2.split('-')[0]
  145. rast1 = rast1.encode('ascii', 'ignore')
  146. rast2 = rast2.encode('ascii', 'ignore')
  147. datalist.append((rast1,rast2))
  148. return datalist
  149. except GException, e:
  150. GError(parent = self,
  151. message = e.value)
  152. return None
  153. def CreatePlotList(self):
  154. """!Make list of elements to plot
  155. """
  156. # graph the cell value, frequency pairs for the histogram
  157. self.plotlist = []
  158. for rpair in self.rasterList:
  159. if 'datalist' not in self.raster[rpair] or \
  160. self.raster[rpair]['datalist'] is None:
  161. continue
  162. if len(self.raster[rpair]['datalist']) > 0:
  163. col = wx.Colour(self.raster[rpair]['pcolor'][0],
  164. self.raster[rpair]['pcolor'][1],
  165. self.raster[rpair]['pcolor'][2],
  166. 255)
  167. scatterpoints = plot.PolyMarker(self.raster[rpair]['datalist'],
  168. legend = ' ' + self.raster[rpair]['plegend'],
  169. colour = col,size = self.raster[rpair]['psize'],
  170. fillstyle = self.ptfilldict[self.raster[rpair]['pfill']],
  171. marker = self.raster[rpair]['ptype'])
  172. self.plotlist.append(scatterpoints)
  173. return self.plotlist
  174. def Update(self):
  175. """!Update histogram after changing options
  176. """
  177. self.SetGraphStyle()
  178. p = self.CreatePlotList()
  179. if p:
  180. self.DrawPlot(p)
  181. else:
  182. GMessage(_("Nothing to plot."), parent = self)
  183. def OnRegression(self, event):
  184. """!Displays regression information in messagebox
  185. """
  186. message = []
  187. title = _('Regression Statistics for Scatterplot(s)')
  188. for rpair in self.rasterList:
  189. if isinstance(rpair, tuple) == False: continue
  190. rast1, rast2 = rpair
  191. rast1 = rast1.split('@')[0]
  192. rast2 = rast2.split('@')[0]
  193. ret = grass.parse_command('r.regression.line',
  194. map1 = rast1,
  195. map2 = rast2,
  196. flags = 'g', quiet = True,
  197. parse = (grass.parse_key_val, { 'sep' : '=' }))
  198. eqtitle = _('Regression equation for raster map <%(rast1)s> vs. <%(rast2)s>:\n\n') % \
  199. { 'rast1' : rast1,
  200. 'rast2' : rast2 }
  201. eq = ' %s = %s + %s(%s)\n\n' % (rast2, ret['a'], ret['b'], rast1)
  202. num = 'N = %s\n' % ret['N']
  203. rval = 'R = %s\n' % ret['R']
  204. rsq = 'R-squared = %f\n' % pow(float(ret['R']), 2)
  205. ftest = 'F = %s\n' % ret['F']
  206. str = eqtitle + eq + num + rval + rsq + ftest
  207. message.append(str)
  208. stats = PlotStatsFrame(self, id = wx.ID_ANY, message = message,
  209. title = title)
  210. if stats.Show() == wx.ID_CLOSE:
  211. stats.Destroy()
  212. class ScatterToolbar(BaseToolbar):
  213. """!Toolbar for bivariate scatterplots of raster map pairs
  214. """
  215. def __init__(self, parent):
  216. BaseToolbar.__init__(self, parent)
  217. self.InitToolbar(self._toolbarData())
  218. # realize the toolbar
  219. self.Realize()
  220. def _toolbarData(self):
  221. """!Toolbar data"""
  222. return self._getToolbarData((('addraster', BaseIcons["addRast"],
  223. self.parent.OnSelectRaster),
  224. (None, ),
  225. ('draw', PlotIcons["draw"],
  226. self.parent.OnCreateScatter),
  227. ('erase', BaseIcons["erase"],
  228. self.parent.OnErase),
  229. ('drag', BaseIcons['pan'],
  230. self.parent.OnDrag),
  231. ('zoom', BaseIcons['zoomIn'],
  232. self.parent.OnZoom),
  233. ('unzoom', BaseIcons['zoomBack'],
  234. self.parent.OnRedraw),
  235. (None, ),
  236. ('statistics', PlotIcons['statistics'],
  237. self.parent.OnRegression),
  238. ('image', BaseIcons["saveFile"],
  239. self.parent.SaveToFile),
  240. ('print', BaseIcons["print"],
  241. self.parent.PrintMenu),
  242. (None, ),
  243. ('settings', PlotIcons["options"],
  244. self.parent.PlotOptionsMenu),
  245. ('quit', PlotIcons["quit"],
  246. self.parent.OnQuit),
  247. ))