scatter.py 11 KB

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