mcalc_builder.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. """!
  2. @package mcalc_builder.py
  3. @brief Map calculator, wrapper for r.mapcalc
  4. Classes:
  5. - MapCalcFrame
  6. (C) 2008, 2010 by the GRASS Development Team
  7. This program is free software under the GNU General Public License
  8. (>=v2). Read the file COPYING that comes with GRASS for details.
  9. @author Michael Barton, Arizona State University
  10. @author Martin Landa <landa.martin gmail.com>
  11. """
  12. import os
  13. import sys
  14. import time
  15. import globalvar
  16. if not os.getenv("GRASS_WXBUNDLED"):
  17. globalvar.CheckForWx()
  18. import wx
  19. import gcmd
  20. import gselect
  21. try:
  22. import subprocess
  23. except:
  24. sys.path.append(os.path.join(globalvar.ETCWXDIR, "compat"))
  25. import subprocess
  26. from preferences import globalSettings as UserSettings
  27. class MapCalcFrame(wx.Frame):
  28. """!Mapcalc Frame class. Calculator-style window to create and run
  29. r(3).mapcalc statements
  30. """
  31. def __init__(self, parent, id = wx.ID_ANY, title = _('Map calculator'),
  32. rast3d = False, style = wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER, **kwargs):
  33. self.parent = parent
  34. if self.parent:
  35. self.log = self.parent.GetLogWindow()
  36. else:
  37. self.log = None
  38. self.rast3d = rast3d
  39. wx.Frame.__init__(self, parent, id = id, title = title, **kwargs)
  40. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
  41. self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
  42. #
  43. # variables
  44. #
  45. self.heading = _('mapcalc statement')
  46. self.funct_list = [
  47. 'abs(x)',
  48. 'acos(x)',
  49. 'asin(x)',
  50. 'atan(x)',
  51. 'atan(x,y)',
  52. 'cos(x)',
  53. 'double(x)',
  54. 'eval([x,y,...,]z)',
  55. 'exp(x)',
  56. 'exp(x,y)',
  57. 'float(x)',
  58. 'graph(x,x1,y1[x2,y2..])',
  59. 'if(x)',
  60. 'if(x,a)',
  61. 'if(x,a,b)',
  62. 'if(x,a,b,c)',
  63. 'int(x)',
  64. 'isnull(x)',
  65. 'log(x)',
  66. 'log(x,b)',
  67. 'max(x,y[,z...])',
  68. 'median(x,y[,z...])',
  69. 'min(x,y[,z...])',
  70. 'mode(x,y[,z...])',
  71. 'not(x)',
  72. 'pow(x,y)',
  73. 'rand(a,b)',
  74. 'round(x)',
  75. 'sin(x)',
  76. 'sqrt(x)',
  77. 'tan(x)',
  78. 'xor(x,y)',
  79. 'row()',
  80. 'col()',
  81. 'x()',
  82. 'y()',
  83. 'ewres()',
  84. 'nsres()',
  85. 'null()'
  86. ]
  87. if self.rast3d:
  88. indx = self.funct_list.index('y()') +1
  89. self.funct_list.insert(indx, 'z()')
  90. indx = self.funct_list.index('nsres()') +1
  91. self.funct_list.insert(indx, 'tbres()')
  92. maplabel = _('3D raster map')
  93. element = 'rast3d'
  94. else:
  95. maplabel = _('raster map')
  96. element = 'cell'
  97. self.operatorBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  98. label=" %s " % _('Operators'))
  99. self.operandBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  100. label=" %s " % _('Operands'))
  101. self.expressBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  102. label=" %s " % _('Expression'))
  103. #
  104. # Buttons
  105. #
  106. self.btn_clear = wx.Button(parent = self.panel, id = wx.ID_CLEAR)
  107. self.btn_help = wx.Button(parent = self.panel, id = wx.ID_HELP)
  108. self.btn_run = wx.Button(parent = self.panel, id = wx.ID_ANY, label = _("&Run"))
  109. self.btn_run.SetDefault()
  110. self.btn_close = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
  111. self.btn = dict()
  112. self.btn['pow'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "^")
  113. self.btn['pow'].SetToolTipString(_('exponent'))
  114. self.btn['div'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "/")
  115. self.btn['div'].SetToolTipString(_('divide'))
  116. self.btn['add'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "+")
  117. self.btn['add'].SetToolTipString(_('add'))
  118. self.btn['minus'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "-")
  119. self.btn['minus'].SetToolTipString(_('subtract'))
  120. self.btn['mod'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "%")
  121. self.btn['mod'].SetToolTipString(_('modulus'))
  122. self.btn['mult'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "*")
  123. self.btn['mult'].SetToolTipString(_('multiply'))
  124. self.btn['paren'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "( )")
  125. self.btn['lshift'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "<<")
  126. self.btn['lshift'].SetToolTipString(_('left shift'))
  127. self.btn['rshift'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = ">>")
  128. self.btn['rshift'].SetToolTipString(_('right shift'))
  129. self.btn['rshiftu'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = ">>>")
  130. self.btn['rshiftu'].SetToolTipString(_('right shift (unsigned)'))
  131. self.btn['gt'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = ">")
  132. self.btn['gt'].SetToolTipString(_('greater than'))
  133. self.btn['gteq'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = ">=")
  134. self.btn['gteq'].SetToolTipString(_('greater than or equal to'))
  135. self.btn['lt'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "<")
  136. self.btn['lt'].SetToolTipString(_('less than or equal to'))
  137. self.btn['lteq'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "<=")
  138. self.btn['lteq'].SetToolTipString(_('less than'))
  139. self.btn['eq'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "==")
  140. self.btn['eq'].SetToolTipString(_('equal to'))
  141. self.btn['noteq'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "!=")
  142. self.btn['noteq'].SetToolTipString(_('not equal to'))
  143. self.btn['compl'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "~")
  144. self.btn['compl'].SetToolTipString(_('one\'s complement'))
  145. self.btn['not'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "!")
  146. self.btn['not'].SetToolTipString(_('NOT'))
  147. self.btn['andbit'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = '&')
  148. self.btn['andbit'].SetToolTipString(_('bitwise AND'))
  149. self.btn['orbit'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "|")
  150. self.btn['orbit'].SetToolTipString(_('bitwise OR'))
  151. self.btn['and'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "&&&")
  152. self.btn['and'].SetToolTipString(_('logical AND'))
  153. self.btn['andnull'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "&&&&&")
  154. self.btn['andnull'].SetToolTipString(_('logical AND (ignores NULLs'))
  155. self.btn['or'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "||")
  156. self.btn['or'].SetToolTipString(_('logical OR'))
  157. self.btn['ornull'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "|||")
  158. self.btn['ornull'].SetToolTipString(_('logical OR (ignores NULLs'))
  159. self.btn['cond'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "?:")
  160. self.btn['cond'].SetToolTipString(_('conditional'))
  161. #
  162. # Text area
  163. #
  164. self.text_mcalc = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY, size = (-1, 75),
  165. style = wx.TE_MULTILINE)
  166. wx.CallAfter(self.text_mcalc.SetFocus)
  167. #
  168. # Map and function insertion text and ComboBoxes
  169. self.newmaplabel = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
  170. label= _('Name for new %s to create') % maplabel)
  171. self.newmaptxt = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY, size=(250, -1))
  172. self.mapsellabel = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
  173. label = _('Insert existing %s') % maplabel)
  174. self.mapselect = gselect.Select(parent = self.panel, id = wx.ID_ANY, size = (250, -1),
  175. type = element, multiple = False)
  176. self.functlabel = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
  177. label = _('Insert mapcalc function'))
  178. self.function = wx.ComboBox(parent = self.panel, id = wx.ID_ANY,
  179. size = (250, -1), choices = self.funct_list,
  180. style = wx.CB_DROPDOWN |
  181. wx.CB_READONLY | wx.TE_PROCESS_ENTER)
  182. self.overwrite = wx.CheckBox(parent = self, id = wx.ID_ANY,
  183. label=_("Allow output files to overwrite existing files"))
  184. self.overwrite.SetValue(UserSettings.Get(group='cmd', key='overwrite', subkey='enabled'))
  185. #
  186. # Bindings
  187. #
  188. for btn in self.btn.keys():
  189. self.btn[btn].Bind(wx.EVT_BUTTON, self.AddMark)
  190. self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
  191. self.btn_clear.Bind(wx.EVT_BUTTON, self.OnClear)
  192. self.btn_run.Bind(wx.EVT_BUTTON, self.OnMCalcRun)
  193. self.btn_help.Bind(wx.EVT_BUTTON, self.OnHelp)
  194. self.mapselect.Bind(wx.EVT_TEXT, self.OnSelect)
  195. self.function.Bind(wx.EVT_COMBOBOX, self.OnSelect)
  196. self.function.Bind(wx.EVT_TEXT_ENTER, self.OnSelect)
  197. self._layout()
  198. self.SetMinSize(self.GetBestSize())
  199. def _layout(self):
  200. sizer = wx.BoxSizer(wx.VERTICAL)
  201. controlSizer = wx.BoxSizer(wx.HORIZONTAL)
  202. operatorSizer = wx.StaticBoxSizer(self.operatorBox, wx.HORIZONTAL)
  203. buttonSizer1 = wx.GridBagSizer(5, 1)
  204. buttonSizer1.Add(item = self.btn['add'], pos = (0,0))
  205. buttonSizer1.Add(item = self.btn['minus'], pos = (0,1))
  206. buttonSizer1.Add(item = self.btn['mod'], pos = (5,0))
  207. buttonSizer1.Add(item = self.btn['mult'], pos = (1,0))
  208. buttonSizer1.Add(item = self.btn['div'], pos = (1,1))
  209. buttonSizer1.Add(item = self.btn['pow'], pos = (5,1))
  210. buttonSizer1.Add(item = self.btn['gt'], pos = (2,0))
  211. buttonSizer1.Add(item = self.btn['gteq'], pos = (2,1))
  212. buttonSizer1.Add(item = self.btn['eq'], pos = (4,0))
  213. buttonSizer1.Add(item = self.btn['lt'], pos = (3,0))
  214. buttonSizer1.Add(item = self.btn['lteq'], pos = (3,1))
  215. buttonSizer1.Add(item = self.btn['noteq'], pos = (4,1))
  216. buttonSizer2 = wx.GridBagSizer(5, 1)
  217. buttonSizer2.Add(item = self.btn['and'], pos = (0,0))
  218. buttonSizer2.Add(item = self.btn['andbit'], pos = (1,0))
  219. buttonSizer2.Add(item = self.btn['andnull'], pos = (2,0))
  220. buttonSizer2.Add(item = self.btn['or'], pos = (0,1))
  221. buttonSizer2.Add(item = self.btn['orbit'], pos = (1,1))
  222. buttonSizer2.Add(item = self.btn['ornull'], pos = (2,1))
  223. buttonSizer2.Add(item = self.btn['lshift'], pos = (3,0))
  224. buttonSizer2.Add(item = self.btn['rshift'], pos = (3,1))
  225. buttonSizer2.Add(item = self.btn['rshiftu'], pos = (4,0))
  226. buttonSizer2.Add(item = self.btn['cond'], pos = (5,0))
  227. buttonSizer2.Add(item = self.btn['compl'], pos = (5,1))
  228. buttonSizer2.Add(item = self.btn['not'], pos = (4,1))
  229. operandSizer = wx.StaticBoxSizer(self.operandBox, wx.HORIZONTAL)
  230. buttonSizer3 = wx.GridBagSizer(7, 1)
  231. buttonSizer3.Add(item = self.newmaplabel, pos = (0, 0),
  232. span = (1, 2), flag = wx.ALIGN_CENTER)
  233. buttonSizer3.Add(item = self.newmaptxt, pos = (1,0),
  234. span = (1, 2))
  235. buttonSizer3.Add(item = self.mapsellabel, pos = (2,0),
  236. span = (1,2), flag = wx.ALIGN_CENTER)
  237. buttonSizer3.Add(item = self.mapselect, pos = (3,0),
  238. span = (1,2))
  239. buttonSizer3.Add(item = self.functlabel, pos = (4,0),
  240. span = (1,2), flag = wx.ALIGN_CENTER)
  241. buttonSizer3.Add(item = self.function, pos = (5,0),
  242. span = (1,2))
  243. buttonSizer3.Add(item = self.btn['paren'], pos = (6, 0),
  244. span = (1,1), flag = wx.ALIGN_LEFT)
  245. buttonSizer3.Add(item = self.btn_clear, pos = (6,1),
  246. span = (1,1), flag = wx.ALIGN_RIGHT)
  247. buttonSizer4 = wx.BoxSizer(wx.HORIZONTAL)
  248. buttonSizer4.Add(item = self.btn_close,
  249. flag = wx.ALL, border = 5)
  250. buttonSizer4.Add(item = self.btn_run,
  251. flag = wx.ALL, border = 5)
  252. buttonSizer4.Add(item = self.btn_help,
  253. flag = wx.ALL, border = 5)
  254. operatorSizer.Add(item = buttonSizer1, proportion = 0,
  255. flag = wx.ALL | wx.EXPAND, border = 5)
  256. operatorSizer.Add(item = buttonSizer2, proportion = 0,
  257. flag = wx.TOP | wx.BOTTOM | wx.RIGHT | wx.EXPAND, border = 5)
  258. operandSizer.Add(item = buttonSizer3, proportion = 0,
  259. flag = wx.TOP | wx.BOTTOM | wx.RIGHT, border = 5)
  260. controlSizer.Add(item = operatorSizer, proportion = 1,
  261. flag = wx.RIGHT, border = 5)
  262. controlSizer.Add(item = operandSizer, proportion = 0,
  263. flag = wx.EXPAND)
  264. expressSizer = wx.StaticBoxSizer(self.expressBox, wx.HORIZONTAL)
  265. expressSizer.Add(item = self.text_mcalc, proportion = 1,
  266. flag = wx.EXPAND)
  267. sizer.Add(item = controlSizer, proportion = 0,
  268. flag = wx.EXPAND | wx.ALL,
  269. border = 5)
  270. sizer.Add(item = expressSizer, proportion = 1,
  271. flag = wx.EXPAND | wx.LEFT | wx.RIGHT,
  272. border = 5)
  273. sizer.Add(item = self.overwrite, flag = wx.EXPAND | wx.LEFT | wx.RIGHT,
  274. border = 5)
  275. sizer.Add(item = buttonSizer4, proportion = 0,
  276. flag = wx.ALIGN_RIGHT | wx.ALL, border = 1)
  277. self.panel.SetAutoLayout(True)
  278. self.panel.SetSizer(sizer)
  279. sizer.Fit(self.panel)
  280. self.Fit()
  281. self.Layout()
  282. def AddMark(self,event):
  283. """!Sends operators to insertion method
  284. """
  285. if event.GetId() == self.btn['compl'].GetId(): mark = "~"
  286. elif event.GetId() == self.btn['not'].GetId(): mark = "!"
  287. elif event.GetId() == self.btn['pow'].GetId(): mark = "^"
  288. elif event.GetId() == self.btn['div'].GetId(): mark = "/"
  289. elif event.GetId() == self.btn['add'].GetId(): mark = "+"
  290. elif event.GetId() == self.btn['minus'].GetId(): mark = "-"
  291. elif event.GetId() == self.btn['mod'].GetId(): mark = "%"
  292. elif event.GetId() == self.btn['mult'].GetId(): mark = "*"
  293. elif event.GetId() == self.btn['lshift'].GetId(): mark = "<<"
  294. elif event.GetId() == self.btn['rshift'].GetId(): mark = ">>"
  295. elif event.GetId() == self.btn['rshiftu'].GetId(): mark = ">>>"
  296. elif event.GetId() == self.btn['gt'].GetId(): mark = ">"
  297. elif event.GetId() == self.btn['gteq'].GetId(): mark = ">="
  298. elif event.GetId() == self.btn['lt'].GetId(): mark = "<"
  299. elif event.GetId() == self.btn['lteq'].GetId(): mark = "<="
  300. elif event.GetId() == self.btn['eq'].GetId(): mark = "=="
  301. elif event.GetId() == self.btn['noteq'].GetId(): mark = "!="
  302. elif event.GetId() == self.btn['andbit'].GetId(): mark = "&"
  303. elif event.GetId() == self.btn['orbit'].GetId(): mark = "|"
  304. elif event.GetId() == self.btn['or'].GetId(): mark = "||"
  305. elif event.GetId() == self.btn['ornull'].GetId(): mark = "|||"
  306. elif event.GetId() == self.btn['and'].GetId(): mark = "&&"
  307. elif event.GetId() == self.btn['andnull'].GetId(): mark = "&&&"
  308. elif event.GetId() == self.btn['cond'].GetId(): mark = "?:"
  309. elif event.GetId() == self.btn['paren'].GetId(): mark = "()"
  310. self._addSomething(mark)
  311. def OnSelect(self, event):
  312. """!Gets raster map or function selection and send it to
  313. insertion method
  314. """
  315. item = event.GetString()
  316. self._addSomething(item)
  317. def _addSomething(self,what):
  318. """!Inserts operators, map names, and functions into text area
  319. """
  320. self.text_mcalc.SetFocus()
  321. mcalcstr = self.text_mcalc.GetValue()
  322. position = self.text_mcalc.GetInsertionPoint()
  323. newmcalcstr = mcalcstr[:position]
  324. position_offset = 0
  325. try:
  326. if newmcalcstr[-1] != ' ':
  327. newmcalcstr += ' '
  328. position_offset += 1
  329. except:
  330. pass
  331. newmcalcstr += what
  332. position_offset += len(what)
  333. newmcalcstr += ' ' + mcalcstr[position:]
  334. self.text_mcalc.SetValue(newmcalcstr)
  335. self.text_mcalc.SetInsertionPoint(position + position_offset)
  336. self.text_mcalc.Update()
  337. def OnMCalcRun(self,event):
  338. """!Builds and runs r.mapcalc statement
  339. """
  340. name = self.newmaptxt.GetValue().strip()
  341. if not name:
  342. gcmd.GMessage(parent = self,
  343. message = _("You must enter the name of a new map to create"),
  344. msgType = 'info')
  345. return
  346. if not self.text_mcalc.GetValue().strip():
  347. gcmd.GMessage(parent = self,
  348. message = _("You must enter a mapcalc statement to create a new map"),
  349. msgType = 'info')
  350. return
  351. mctxt = self.text_mcalc.GetValue().strip().replace("\n"," ")
  352. mctxt = mctxt.replace(" " , "")
  353. if self.rast3d:
  354. prg = 'r3.mapcalc'
  355. else:
  356. prg = 'r.mapcalc'
  357. if self.log:
  358. cmd = [prg, str('expression=%s = %s' % (name, mctxt))]
  359. if self.overwrite.IsChecked():
  360. cmd.append('--overwrite')
  361. self.log.RunCmd(cmd)
  362. self.parent.Raise()
  363. else:
  364. if self.overwrite.IsChecked():
  365. overwrite = True
  366. else:
  367. overwrite = False
  368. gcmd.RunCommand(prg,
  369. expression = "%s=%s" % (name, mctxt),
  370. overwrite = overwrite)
  371. def OnClear(self, event):
  372. """!Clears text area
  373. """
  374. self.text_mcalc.SetValue('')
  375. def OnHelp(self, event):
  376. """!Launches r.mapcalc help
  377. """
  378. gcmd.RunCommand('g.manual', entry = 'r.mapcalc')
  379. def OnClose(self,event):
  380. """!Close window"""
  381. self.Destroy()
  382. if __name__ == "__main__":
  383. app = wx.App(0)
  384. frame = MapCalcFrame(None)
  385. frame.Show()
  386. app.MainLoop()