scatter.py 12 KB

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