scatter.py 11 KB

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