scatter.py 12 KB

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