v.krige.py 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  1. #!/usr/bin/env python
  2. """
  3. MODULE: v.krige
  4. AUTHOR(S): Anne Ghisla <a.ghisla AT gmail.com>
  5. PURPOSE: Performs ordinary or block kriging
  6. DEPENDS: R 2.x, packages gstat, maptools and spgrass6, optional: automap
  7. COPYRIGHT: (C) 2009 by the GRASS Development Team
  8. This program is free software under the GNU General Public
  9. License (>=v2). Read the file COPYING that comes with GRASS
  10. for details.
  11. """
  12. ## g.parser informations
  13. #%module
  14. #% description: Performs ordinary or block kriging.
  15. #% keywords: vector, interpolation, kriging
  16. #%end
  17. #%option
  18. #% key: input
  19. #% type: string
  20. #% gisprompt: old,vector,vector
  21. #% description: Name of point vector map containing sample data
  22. #% required: yes
  23. #%end
  24. #%option
  25. #% key: column
  26. #% type: string
  27. #% gisprompt: old,dbcolumn,dbcolumn
  28. #% description: Name of attribute column with numerical value to be interpolated
  29. #% required : yes
  30. #%end
  31. #%option
  32. #% key: output
  33. #% type: string
  34. #% gisprompt: new,cell,raster
  35. #% label: Name for output raster map
  36. #% description: If omitted, will be <input name>_kriging
  37. #% required : no
  38. #%end
  39. #%option
  40. #% key: package
  41. #% type: string
  42. #% options: gstat
  43. #% answer: gstat
  44. #% description: R package to use
  45. #% required: no
  46. #%end
  47. #%option
  48. #% key: model
  49. #% type: string
  50. #% options: Exp,Sph,Gau,Mat,Lin
  51. #% multiple: yes
  52. #% label: Variogram model(s)
  53. #% description: Leave empty to test all models (requires automap)
  54. #% required: no
  55. #%end
  56. #%option
  57. #% key: block
  58. #% type: integer
  59. #% multiple: no
  60. #% label: Block size (square block)
  61. #% description: Block size. Used by block kriging.
  62. #% required: no
  63. #%end
  64. #%option
  65. #% key: range
  66. #% type: integer
  67. #% label: Range value
  68. #% description: Automatically fixed if not set
  69. #% required : no
  70. #%end
  71. #%option
  72. #% key: nugget
  73. #% type: integer
  74. #% label: Nugget value
  75. #% description: Automatically fixed if not set
  76. #% required : no
  77. #%end
  78. #%option
  79. #% key: sill
  80. #% type: integer
  81. #% label: Sill value
  82. #% description: Automatically fixed if not set
  83. #% required : no
  84. #%end
  85. #%option
  86. #% key: output_var
  87. #% type: string
  88. #% gisprompt: new,cell,raster
  89. #% label: Name for output variance raster map
  90. #% description: If omitted, will be <input name>_kriging_var
  91. #% required : no
  92. #%end
  93. import os, sys
  94. from tempfile import gettempdir
  95. import time
  96. import thread
  97. GUIModulesPath = os.path.join(os.getenv("GISBASE"), "etc", "wxpython", "gui_modules")
  98. sys.path.append(GUIModulesPath)
  99. GUIPath = os.path.join(os.getenv("GISBASE"), "etc", "wxpython")
  100. sys.path.append(GUIPath)
  101. import globalvar
  102. if not os.getenv("GRASS_WXBUNDLED"):
  103. globalvar.CheckForWx()
  104. import gselect
  105. import goutput
  106. import menuform
  107. from preferences import globalSettings as UserSettings
  108. #import help
  109. import wx
  110. import wx.lib.flatnotebook as FN
  111. import wx.lib.plot as plot # for plotting the variogram.
  112. ### i18N
  113. import gettext
  114. gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
  115. ### dependencies to be checked once, as they are quite time-consuming. cfr. grass.parser.
  116. # GRASS binding
  117. try:
  118. import grass.script as grass
  119. except ImportError:
  120. sys.exit(_("No GRASS-python library found."))
  121. # move other checks in functions, as R?
  122. # globals
  123. maxint = 1e6 # instead of sys.maxint, not working with SpinCtrl on 64bit [reported by Bob Moskovitz]
  124. Region = grass.region()
  125. Command = None
  126. InputData = None
  127. Variogram = None
  128. VariogramFunction = None
  129. robjects = None
  130. rinterface = None
  131. #classes in alphabetical order. methods in logical order :)
  132. class Controller():
  133. """ Executes analysis. For the moment, only with gstat functions."""
  134. def ImportMap(self, map, column):
  135. """ Imports GRASS map as SpatialPointsDataFrame and adds x/y columns to attribute table.
  136. Checks for NULL values in the provided column and exits if they are present."""
  137. #@NOTE: new way with R - as it doesn't alter original data
  138. Rpointmap = robjects.r.readVECT6(map, type = 'point')
  139. # checks if x,y columns are present in dataframe. If they do are present, but with different names,
  140. # they'll be duplicated.
  141. if "x" not in robjects.r.names(Rpointmap):
  142. # extract coordinates with S4 method
  143. coordinatesPreDF = robjects.r['as.data.frame'](robjects.r.coordinates(Rpointmap))
  144. coordinatesDF = robjects.r['data.frame'](x = coordinatesPreDF.r['coords.x1'][0],
  145. y = coordinatesPreDF.r['coords.x2'][0])
  146. # match coordinates with data slot of SpatialPointsDataFrame - maptools function
  147. # match is done on row.names
  148. Rpointmap = robjects.r.spCbind(Rpointmap, coordinatesDF)
  149. # GRASS checks for null values in the chosen column. R can hardly handle column as a variable,
  150. # looks for a hardcoded string.
  151. cols = grass.vector_columns(map=map, layer=1)
  152. nulls = int(grass.parse_command('v.univar',
  153. map=map,
  154. column=column,
  155. type='point',
  156. parse = (grass.parse_key_val,
  157. {'sep':': '}
  158. )
  159. )['number of NULL attributes'])
  160. if nulls > 0:
  161. grass.fatal(_("%d NULL value(s) in the selected column - unable to perform kriging.") % nulls)
  162. return Rpointmap
  163. def CreateGrid(self, inputdata):
  164. Grid = robjects.r.gmeta2grd()
  165. # addition of coordinates columns into dataframe.
  166. coordinatesDF = robjects.r['as.data.frame'](robjects.r.coordinates(Grid))
  167. data = robjects.r['data.frame'](x = coordinatesDF.r['s1'][0],
  168. y = coordinatesDF.r['s2'][0],
  169. k = robjects.r.rep(1, Region['cols']*Region['rows']))
  170. GridPredicted = robjects.r.SpatialGridDataFrame(Grid,
  171. data,
  172. proj4string = robjects.r.CRS(robjects.r.proj4string(inputdata)))
  173. return GridPredicted
  174. def ComposeFormula(self, column, isblock, inputdata):
  175. if isblock is True:
  176. predictor = 'x+y'
  177. else:
  178. predictor = 1
  179. Formula = robjects.r['as.formula'](robjects.r.paste(column, "~", predictor))
  180. #print Formula
  181. return Formula
  182. def FitVariogram(self, formula, inputdata, sill, nugget, range, model = ''):
  183. """ Fits variogram either automagically either specifying all parameters.
  184. Returns a list containing data and model variograms. """
  185. Variograms = {}
  186. if model is '':
  187. robjects.r.require('automap')
  188. DottedParams = {}
  189. #print (nugget.r_repr(), sill, range)
  190. DottedParams['fix.values'] = robjects.r.c(nugget, range, sill)
  191. VariogramModel = robjects.r.autofitVariogram(formula, inputdata, **DottedParams)
  192. #print robjects.r.warnings()
  193. Variograms['datavariogram'] = VariogramModel.r['exp_var'][0]
  194. Variograms['variogrammodel'] = VariogramModel.r['var_model'][0]
  195. # obtain the model name. *Too* complicated to get the string instead of level, unlike R does.
  196. VariogramAsDF = robjects.r['as.data.frame'](VariogramModel.r['var_model'][0]) # force conversion
  197. ModelDF = VariogramAsDF.r['model'][0]
  198. Variograms['model'] = robjects.r.levels(ModelDF).subset(ModelDF[1])[0]
  199. else:
  200. DataVariogram = robjects.r['variogram'](formula, inputdata)
  201. VariogramModel = robjects.r['fit.variogram'](DataVariogram,
  202. model = robjects.r.vgm(psill = sill,
  203. model = model,
  204. nugget = nugget,
  205. range = range))
  206. Variograms['datavariogram'] = DataVariogram
  207. Variograms['variogrammodel'] = VariogramModel
  208. Variograms['model'] = model
  209. return Variograms
  210. def DoKriging(self, formula, inputdata, grid, model, block):
  211. DottedParams = {'debug.level': -1} # let krige() print percentage status
  212. if block is not '': #@FIXME(anne): but it's a string!! and krige accepts it!!
  213. DottedParams['block'] = block
  214. #print DottedParams
  215. KrigingResult = robjects.r.krige(formula, inputdata, grid, model, **DottedParams)
  216. return KrigingResult
  217. def ExportMap(self, map, column, name, overwrite, command, variograms):
  218. # add kriging parameters to raster map history
  219. robjects.r.writeRAST6(map, vname = name, zcol = column, overwrite = overwrite)
  220. grass.run_command('r.support',
  221. map = name,
  222. title = 'Kriging output',
  223. history = 'Issued from command v.krige ' + command)
  224. if command.find('model') is -1: # if the command has no model option, add automap chosen model
  225. grass.run_command('r.support',
  226. map = name,
  227. history = 'Model chosen by automatic fitting: ' + variograms['model'])
  228. def Run(self, input, column, output, package, sill, nugget, range, logger, \
  229. overwrite, model, block, output_var, command, **kwargs):
  230. """ Wrapper for all functions above. """
  231. logger.message(_("Processing %d cells. Computing time raises exponentially with resolution." % Region['cells']))
  232. logger.message(_("Importing data..."))
  233. if globals()["InputData"] is None:
  234. globals()["InputData"] = self.ImportMap(input, column)
  235. # and from here over, InputData refers to the global variable
  236. #print(robjects.r.slot(InputData, 'data').names)
  237. logger.message(_("Data successfully imported."))
  238. GridPredicted = self.CreateGrid(InputData)
  239. logger.message(_("Fitting variogram..."))
  240. isblock = block is not ''
  241. Formula = self.ComposeFormula(column, isblock, InputData)
  242. if globals()["Variogram"] is None:
  243. globals()["Variogram"] = self.FitVariogram(Formula,
  244. InputData,
  245. model = model,
  246. sill = sill,
  247. nugget = nugget,
  248. range = range)
  249. logger.message(_("Variogram fitted."))
  250. logger.message(_("Kriging..."))
  251. KrigingResult = self.DoKriging(Formula, InputData, GridPredicted, Variogram['variogrammodel'], block) # using global ones
  252. logger.message(_("Kriging performed."))
  253. self.ExportMap(map = KrigingResult,
  254. column='var1.pred',
  255. name = output,
  256. overwrite = overwrite,
  257. command = command,
  258. variograms = Variogram)
  259. if output_var is not '':
  260. self.ExportMap(map = KrigingResult,
  261. column='var1.var',
  262. name = output_var,
  263. overwrite = overwrite,
  264. command = command,
  265. variograms = Variogram)
  266. class KrigingPanel(wx.Panel):
  267. """ Main panel. Contains all widgets except Menus and Statusbar. """
  268. def __init__(self, parent, *args, **kwargs):
  269. wx.Panel.__init__(self, parent, *args, **kwargs)
  270. self.parent = parent
  271. self.border = 4
  272. # 1. Input data
  273. InputBoxSizer = wx.StaticBoxSizer(wx.StaticBox(self, id = wx.ID_ANY, label = _("Input Data")),
  274. orient = wx.HORIZONTAL)
  275. flexSizer = wx.FlexGridSizer(cols = 3, hgap = 5, vgap = 5)
  276. flexSizer.AddGrowableCol(1)
  277. flexSizer.Add(item = wx.StaticText(self, id = wx.ID_ANY, label = _("Point dataset:")),
  278. flag = wx.ALIGN_CENTER_VERTICAL)
  279. self.InputDataMap = gselect.VectorSelect(parent = self,
  280. ftype = 'points',
  281. updateOnPopup = False)
  282. self.InputDataMap.SetFocus()
  283. flexSizer.Add(item = self.InputDataMap, flag = wx.ALIGN_CENTER_VERTICAL)
  284. RefreshButton = wx.Button(self, id = wx.ID_REFRESH)
  285. RefreshButton.Bind(wx.EVT_BUTTON, self.OnButtonRefresh)
  286. flexSizer.Add(item = RefreshButton, flag = wx.ALIGN_CENTER_VERTICAL)
  287. flexSizer.Add(item = wx.StaticText(self, id = wx.ID_ANY, label = _("Numeric column:")),
  288. flag = wx.ALIGN_CENTER_VERTICAL)
  289. self.InputDataColumn = gselect.ColumnSelect(self, id = wx.ID_ANY)
  290. self.InputDataColumn.SetSelection(0)
  291. flexSizer.Add(item = self.InputDataColumn)
  292. self.InputDataMap.GetChildren()[0].Bind(wx.EVT_TEXT, self.OnInputDataChanged)
  293. InputBoxSizer.Add(item = flexSizer)
  294. # 2. Kriging. In book pages one for each R package. Includes variogram fit.
  295. KrigingSizer = wx.StaticBoxSizer(wx.StaticBox(self, id = wx.ID_ANY, label = _("Kriging")), wx.HORIZONTAL)
  296. self.RPackagesBook = FN.FlatNotebook(parent = self, id = wx.ID_ANY,
  297. style = FN.FNB_BOTTOM |
  298. FN.FNB_NO_NAV_BUTTONS |
  299. FN.FNB_FANCY_TABS | FN.FNB_NO_X_BUTTON)
  300. for Rpackage in ["gstat"]: # , "geoR"]: #@TODO: enable it if/when it'll be implemented.
  301. self.CreatePage(package = Rpackage)
  302. ## Command output. From menuform module, cmdPanel class
  303. self.goutput = goutput.GMConsole(parent = self, margin = False,
  304. pageid = self.RPackagesBook.GetPageCount(),
  305. notebook = self.RPackagesBook)
  306. self.goutputId = self.RPackagesBook.GetPageCount()
  307. self.outpage = self.RPackagesBook.AddPage(self.goutput, text = _("Command output"))
  308. self.RPackagesBook.SetSelection(0)
  309. KrigingSizer.Add(self.RPackagesBook, proportion = 1, flag = wx.EXPAND)
  310. # 3. Output Parameters.
  311. OutputSizer = wx.StaticBoxSizer(wx.StaticBox(self, id = wx.ID_ANY, label = _("Output")), wx.HORIZONTAL)
  312. OutputParameters = wx.GridBagSizer(hgap = 5, vgap = 5)
  313. OutputParameters.AddGrowableCol(1)
  314. OutputParameters.Add(item = wx.StaticText(self, id = wx.ID_ANY, label = _("Name for the output raster map:")),
  315. flag = wx.ALIGN_CENTER_VERTICAL,
  316. pos = (0, 0))
  317. self.OutputMapName = gselect.Select(parent = self, id = wx.ID_ANY,
  318. type = 'raster',
  319. mapsets = [grass.gisenv()['MAPSET']])
  320. OutputParameters.Add(item = self.OutputMapName, flag = wx.EXPAND | wx.ALL,
  321. pos = (0, 1))
  322. self.VarianceRasterCheckbox = wx.CheckBox(self, id = wx.ID_ANY, label = _("Export variance map as well: "))
  323. self.VarianceRasterCheckbox.SetValue(state = True)
  324. OutputParameters.Add(item = self.VarianceRasterCheckbox,
  325. flag = wx.ALIGN_CENTER_VERTICAL,
  326. pos = (1, 0))
  327. self.OutputVarianceMapName = gselect.Select(parent = self, id = wx.ID_ANY,
  328. type = 'raster',
  329. mapsets = [grass.gisenv()['MAPSET']])
  330. self.VarianceRasterCheckbox.Bind(wx.EVT_CHECKBOX, self.OnVarianceCBChecked)
  331. OutputParameters.Add(item = self.OutputVarianceMapName, flag = wx.EXPAND | wx.ALL,
  332. pos = (1, 1))
  333. self.OverwriteCheckBox = wx.CheckBox(self, id = wx.ID_ANY,
  334. label = _("Allow output files to overwrite existing files"))
  335. self.OverwriteCheckBox.SetValue(UserSettings.Get(group='cmd', key='overwrite', subkey='enabled'))
  336. OutputParameters.Add(item = self.OverwriteCheckBox,
  337. pos = (2, 0), span = (1, 2))
  338. OutputSizer.Add(OutputParameters, proportion = 0, flag = wx.EXPAND | wx.ALL, border = self.border)
  339. # 4. Run Button and Quit Button
  340. ButtonSizer = wx.BoxSizer(wx.HORIZONTAL)
  341. HelpButton = wx.Button(self, id = wx.ID_HELP)
  342. HelpButton.Bind(wx.EVT_BUTTON, self.OnHelpButton)
  343. QuitButton = wx.Button(self, id = wx.ID_EXIT)
  344. QuitButton.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
  345. self.RunButton = wx.Button(self, id = wx.ID_ANY, label = _("&Run")) # no stock ID for Run button..
  346. self.RunButton.Bind(wx.EVT_BUTTON, self.OnRunButton)
  347. self.RunButton.Enable(False) # disable it on loading the interface, as input map is not set
  348. ButtonSizer.Add(HelpButton, proportion = 0, flag = wx.ALIGN_LEFT | wx.ALL, border = self.border)
  349. ButtonSizer.Add(QuitButton, proportion = 0, flag = wx.ALIGN_RIGHT | wx.ALL, border = self.border)
  350. ButtonSizer.Add(self.RunButton, proportion = 0, flag = wx.ALIGN_RIGHT | wx.ALL, border = self.border)
  351. # Main Sizer. Add each child sizer as soon as it is ready.
  352. Sizer = wx.BoxSizer(wx.VERTICAL)
  353. Sizer.Add(InputBoxSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = self.border)
  354. Sizer.Add(KrigingSizer, proportion = 1, flag = wx.EXPAND | wx.ALL, border = self.border)
  355. Sizer.Add(OutputSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = self.border)
  356. Sizer.Add(ButtonSizer, proportion = 0, flag = wx.ALIGN_RIGHT | wx.ALL, border = self.border)
  357. self.SetSizerAndFit(Sizer)
  358. # last action of __init__: update imput data list.
  359. # it's performed in the few seconds gap while user examines interface before clicking anything.
  360. #@TODO: implement a splashcreen IF the maps cause a noticeable lag [markus' suggestion]
  361. self.InputDataMap.GetElementList()
  362. def CreatePage(self, package):
  363. """ Creates the three notebook pages, one for each R package """
  364. for package in ["gstat"]:
  365. classobj = eval("RBook"+package+"Panel")
  366. setattr(self, "RBook"+package+"Panel", (classobj(self, id = wx.ID_ANY)))
  367. self.RPackagesBook.AddPage(page = getattr(self, "RBook"+package+"Panel"), text = package)
  368. def OnButtonRefresh(self, event):
  369. """ Forces refresh of list of available layers. """
  370. self.InputDataMap.GetElementList()
  371. def OnCloseWindow(self, event):
  372. """ Cancel button pressed"""
  373. self.parent.Close()
  374. event.Skip()
  375. def OnHelpButton(self, event):
  376. # file = os.path.join(os.getenv("GISBASE"), "docs", "html", "v.krige.html")
  377. # file = os.path.join(os.path.curdir, "description.html")
  378. # @TODO fix HelpWindow
  379. # helpFrame = help.HelpWindow(parent=self, id=wx.ID_ANY,
  380. # title=_("GRASS - Help page for v.krige"),
  381. # size=(640, 480),
  382. # file=file)
  383. # helpFrame.Show(True)
  384. grass.run_command('g.manual', entry = 'v.krige')
  385. event.Skip()
  386. def OnInputDataChanged(self, event):
  387. """ Refreshes list of columns and fills output map name TextCtrl """
  388. MapName = event.GetString()
  389. self.InputDataColumn.InsertColumns(vector = MapName,
  390. layer = 1, excludeKey = True,
  391. type = ['integer', 'double precision'])
  392. self.InputDataColumn.SetSelection(0)
  393. self.RunButton.Enable(self.InputDataColumn.GetSelection() is not -1)
  394. self.RBookgstatPanel.PlotButton.Enable(self.InputDataColumn.GetSelection() is not -1)
  395. if self.InputDataColumn.GetSelection() is not -1:
  396. self.OutputMapName.SetValue(MapName.split("@")[0]+"_kriging")
  397. self.OutputVarianceMapName.SetValue(MapName.split("@")[0]+"_kriging_var")
  398. else:
  399. self.OutputMapName.SetValue('')
  400. self.OutputVarianceMapName.SetValue('')
  401. def OnRunButton(self,event):
  402. """ Execute R analysis. """
  403. #@FIXME: send data to main method instead of running it here.
  404. #-1: get the selected notebook page. The user shall know that [s]he can modify settings in all
  405. # pages, but only the selected one will be executed when Run is pressed.
  406. SelectedPanel = self.RPackagesBook.GetCurrentPage()
  407. if self.RPackagesBook.GetPageText(self.RPackagesBook.GetSelection()) == 'Command output':
  408. self.goutput.WriteError("No parameters for running. Please select \"gstat\" tab, check parameters and re-run.")
  409. return False # no break invoked by above function
  410. # mount command string as it would have been written on CLI
  411. command = ["v.krige", "input=" + self.InputDataMap.GetValue(),
  412. "column=" + self.InputDataColumn.GetValue(),
  413. "output=" + self.OutputMapName.GetValue(),
  414. "package=" + '%s' % self.RPackagesBook.GetPageText(self.RPackagesBook.GetSelection())]
  415. if not hasattr(SelectedPanel, 'VariogramCheckBox') or not SelectedPanel.VariogramCheckBox.IsChecked():
  416. command.append("model=" + '%s' % SelectedPanel.ModelChoicebox.GetStringSelection().split(" ")[0])
  417. for i in ['Sill', 'Nugget', 'Range']:
  418. if getattr(SelectedPanel, i+"ChextBox").IsChecked():
  419. command.append(i.lower() + "=" + '%s' % getattr(SelectedPanel, i+'Ctrl').GetValue())
  420. if SelectedPanel.KrigingRadioBox.GetStringSelection() == "Block kriging":
  421. command.append("block=" + '%s' % SelectedPanel.BlockSpinBox.GetValue())
  422. if self.OverwriteCheckBox.IsChecked():
  423. command.append("--overwrite")
  424. if self.VarianceRasterCheckbox.IsChecked():
  425. command.append("output_var=" + self.OutputVarianceMapName.GetValue())
  426. # give it to the output console
  427. #@FIXME: it runs the command as a NEW instance. Reimports data, recalculates variogram fit..
  428. #otherwise I can use Controller() and mimic RunCmd behaviour.
  429. self.goutput.RunCmd(command, switchPage = True)
  430. def OnVarianceCBChecked(self, event):
  431. self.OutputVarianceMapName.Enable(event.IsChecked())
  432. class KrigingModule(wx.Frame):
  433. """ Kriging module for GRASS GIS. Depends on R and its packages gstat and geoR. """
  434. def __init__(self, parent, *args, **kwargs):
  435. wx.Frame.__init__(self, parent, *args, **kwargs)
  436. # setting properties and all widgettery
  437. self.SetTitle(_("Kriging Module"))
  438. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_dialog.ico'), wx.BITMAP_TYPE_ICO))
  439. self.log = Log(self)
  440. self.CreateStatusBar()
  441. self.log.message(_("Ready."))
  442. self.Panel = KrigingPanel(self)
  443. self.SetMinSize(self.GetBestSize())
  444. self.SetSize(self.GetBestSize())
  445. class Log:
  446. """ The log output is redirected to the status bar of the containing frame. """
  447. def __init__(self, parent):
  448. self.parent = parent
  449. def message(self, text_string):
  450. """ Updates status bar """
  451. self.parent.SetStatusText(text_string.strip())
  452. class RBookPanel(wx.Panel):
  453. """ Generic notebook page with shared widgets and empty kriging functions. """
  454. def __init__(self, parent, *args, **kwargs):
  455. wx.Panel.__init__(self, parent, *args, **kwargs)
  456. self.parent = parent
  457. self.VariogramSizer = wx.StaticBoxSizer(wx.StaticBox(self,
  458. id = wx.ID_ANY,
  459. label = _("Variogram fitting")),
  460. wx.HORIZONTAL)
  461. self.LeftSizer = wx.BoxSizer(wx.VERTICAL)
  462. self.RightSizer = wx.BoxSizer(wx.VERTICAL)
  463. self.ParametersSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
  464. self.VariogramSizer.Add(self.LeftSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = parent.border)
  465. self.VariogramSizer.Add(self.RightSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = parent.border)
  466. # left side of Variogram fitting. The checkboxes and spinctrls.
  467. self.PlotButton = wx.Button(self, id = wx.ID_ANY, label = _("Plot/refresh variogram")) # no stock ID for Run button..
  468. self.PlotButton.Bind(wx.EVT_BUTTON, self.OnPlotButton)
  469. self.PlotButton.Enable(False) # grey it out until a suitable layer is available
  470. self.LeftSizer.Add(self.PlotButton, proportion = 0, flag = wx.ALL, border = parent.border)
  471. self.LeftSizer.Add(self.ParametersSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = parent.border)
  472. self.ParametersList = ["Sill", "Nugget", "Range"]
  473. MinValues = [0,0,1]
  474. for n in self.ParametersList:
  475. setattr(self, n+"ChextBox", wx.CheckBox(self,
  476. id = self.ParametersList.index(n),
  477. label = _(n + ":")))
  478. setattr(self, n+"Ctrl", (wx.SpinCtrl(self,
  479. id = wx.ID_ANY,
  480. min = MinValues[self.ParametersList.index(n)],
  481. max = maxint)))
  482. getattr(self, n+"ChextBox").Bind(wx.EVT_CHECKBOX,
  483. self.UseValue,
  484. id = self.ParametersList.index(n))
  485. setattr(self, n+"Sizer", (wx.BoxSizer(wx.HORIZONTAL)))
  486. self.ParametersSizer.Add(getattr(self, n+"ChextBox"),
  487. flag = wx.ALIGN_CENTER_VERTICAL,
  488. pos = (self.ParametersList.index(n),0))
  489. self.ParametersSizer.Add(getattr(self, n+"Ctrl"),
  490. flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL,
  491. pos = (self.ParametersList.index(n),1))
  492. # right side of the Variogram fitting. The plot area.
  493. #Plot = wx.StaticText(self, id= wx.ID_ANY, label = "Check Plot Variogram to interactively fit model.")
  494. #PlotPanel = wx.Panel(self)
  495. #self.PlotArea = plot.PlotCanvas(PlotPanel)
  496. #self.PlotArea.SetInitialSize(size = (250,250))
  497. #self.RightSizer.Add(PlotPanel, proportion=0, flag= wx.EXPAND|wx.ALL, border=parent.border)
  498. self.KrigingSizer = wx.StaticBoxSizer(wx.StaticBox(self,
  499. id = wx.ID_ANY,
  500. label = _("Kriging techniques")),
  501. wx.VERTICAL)
  502. KrigingList = ["Ordinary kriging", "Block kriging"]#, "Universal kriging"] #@FIXME: i18n on the list?
  503. self.KrigingRadioBox = wx.RadioBox(self,
  504. id = wx.ID_ANY,
  505. choices = KrigingList,
  506. majorDimension = 1,
  507. style = wx.RA_SPECIFY_COLS)
  508. self.KrigingRadioBox.Bind(wx.EVT_RADIOBOX, self.HideBlockOptions)
  509. self.KrigingSizer.Add(self.KrigingRadioBox, proportion = 0, flag = wx.EXPAND | wx.ALL, border = parent.border)
  510. # block kriging parameters. Size.
  511. BlockSizer = wx.BoxSizer(wx.HORIZONTAL)
  512. BlockLabel = wx.StaticText(self, id = wx.ID_ANY, label = _("Block size:"))
  513. self.BlockSpinBox = wx.SpinCtrl(self, id = wx.ID_ANY, min = 1, max = maxint)
  514. self.BlockSpinBox.Enable(False) # default choice is Ordinary kriging so block param is disabled
  515. BlockSizer.Add(BlockLabel, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = parent.border)
  516. BlockSizer.Add(self.BlockSpinBox, flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = parent.border)
  517. self.KrigingSizer.Add(BlockSizer, flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = parent.border)
  518. self.Sizer = wx.BoxSizer(wx.VERTICAL)
  519. self.Sizer.Add(self.VariogramSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = parent.border)
  520. self.Sizer.Add(self.KrigingSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = parent.border)
  521. def HideBlockOptions(self, event):
  522. self.BlockSpinBox.Enable(event.GetInt() == 1)
  523. def OnPlotButton(self,event):
  524. """ Plots variogram with current options. """
  525. pass
  526. def UseValue(self, event):
  527. """ Enables/Disables the SpinCtrl in respect of the checkbox. """
  528. n = self.ParametersList[event.GetId()]
  529. getattr(self, n+"Ctrl").Enable(event.IsChecked())
  530. class RBookgstatPanel(RBookPanel):
  531. """ Subclass of RBookPanel, with specific gstat options and kriging functions. """
  532. def __init__(self, parent, *args, **kwargs):
  533. RBookPanel.__init__(self, parent, *args, **kwargs)
  534. if robjects.r.require('automap')[0]:
  535. self.VariogramCheckBox = wx.CheckBox(self, id = wx.ID_ANY, label = _("Auto-fit variogram"))
  536. self.LeftSizer.Insert(0,
  537. self.VariogramCheckBox,
  538. proportion = 0,
  539. flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL,
  540. border = 4)
  541. self.SetSizerAndFit(self.Sizer)
  542. self.VariogramCheckBox.Bind(wx.EVT_CHECKBOX, self.HideOptions)
  543. self.VariogramCheckBox.SetValue(state = True) # check it by default
  544. ModelFactor = robjects.r.vgm().r['long']
  545. ModelList = robjects.r.levels(ModelFactor[0])
  546. #@FIXME: no other way to let the Python pick it up..
  547. # and this is te wrong place where to load this list. should be at the very beginning.
  548. self.ModelChoicebox = wx.Choice(self, id = wx.ID_ANY, choices = ModelList)
  549. # disable model parameters' widgets by default
  550. for n in ["Sill", "Nugget", "Range"]:
  551. getattr(self, n+"Ctrl").Enable(False)
  552. self.ModelChoicebox.Enable(False)
  553. VariogramSubSizer = wx.BoxSizer(wx.HORIZONTAL)
  554. VariogramSubSizer.Add(item = wx.StaticText(self,
  555. id = wx.ID_ANY,
  556. label = _("Model: ")),
  557. flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL,
  558. border = 4)
  559. VariogramSubSizer.Add(item = self.ModelChoicebox,
  560. flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL,
  561. border = 4)
  562. self.LeftSizer.Insert(2, item = VariogramSubSizer)
  563. self.SetSizerAndFit(self.Sizer)
  564. def HideOptions(self, event):
  565. self.ModelChoicebox.Enable(not event.IsChecked())
  566. for n in ["Sill", "Nugget", "Range"]:
  567. if not event.IsChecked():
  568. getattr(self, n+"Ctrl").Enable(True)
  569. getattr(self, n+ "ChextBox").SetValue(True)
  570. getattr(self, n+ "ChextBox").Enable(False) # grey it out keeping it checked.. improvable
  571. else:
  572. getattr(self, n+"Ctrl").Enable(False)
  573. getattr(self, n+ "ChextBox").SetValue(False)
  574. getattr(self, n+ "ChextBox").Enable(True)
  575. #@FIXME: was for n in self.ParametersSizer.GetChildren(): n.Enable(False) but doesn't work
  576. def OnPlotButton(self,event):
  577. """ Plots variogram with current options. """
  578. ## BIG WARNING: smell of code duplication. Fix this asap.
  579. controller = Controller()
  580. map = self.parent.InputDataMap.GetValue()
  581. column = self.parent.InputDataColumn.GetValue()
  582. # import data or pick them up
  583. if globals()["InputData"] is None:
  584. globals()["InputData"] = controller.ImportMap(map = map,
  585. column = column)
  586. # fit the variogram or pick it up
  587. Formula = controller.ComposeFormula(column = column,
  588. isblock = self.KrigingRadioBox.GetStringSelection() == "Block kriging",
  589. inputdata = globals()['InputData'])
  590. #if globals()["Variogram"] is None:
  591. if hasattr(self, 'VariogramCheckBox') and self.VariogramCheckBox.IsChecked():
  592. self.model = ''
  593. for each in ("Sill","Nugget","Range"):
  594. if getattr(self, each+'ChextBox').IsChecked():
  595. setattr(self, each.lower(), getattr(self, each+"Ctrl").GetValue())
  596. else:
  597. setattr(self, each.lower(), robjects.r('''NA'''))
  598. else:
  599. self.model = self.ModelChoicebox.GetStringSelection().split(" ")[0]
  600. for each in ("Sill","Nugget","Range"):
  601. if getattr(self, each+'ChextBox').IsChecked(): #@FIXME will be removed when chextboxes will be frozen
  602. setattr(self, each.lower(), getattr(self, each+"Ctrl").GetValue())
  603. globals()["Variogram"] = controller.FitVariogram(Formula,
  604. InputData,
  605. model = self.model,
  606. sill = self.sill,
  607. nugget = self.nugget,
  608. range = self.range)
  609. ## A: use wx.plot library. Does not draw lines from f(x), damn.
  610. #
  611. ## convert R dataframe (data variogram) - points
  612. ##print Variogram['datavariogram']
  613. #PointList = []
  614. #for n in range(len(Variogram['datavariogram'].r['dist'][0])): #@FIXME: so less Pythonic!!
  615. # dist = Variogram['datavariogram'].r['dist'][0][n]
  616. # gamma = Variogram['datavariogram'].r['gamma'][0][n]
  617. # PointList.append((dist,gamma))
  618. #DataPoints = plot.PolyMarker(PointList, legend='points', colour='black', marker='circle',size=1)
  619. #
  620. ## convert R dataframe (model variogram) - line
  621. #LineList = []
  622. #textplot = robjects.r.plot(Variogram['datavariogram'], Variogram['variogrammodel'])
  623. #print textplot
  624. ##for n in range(len(Variogram['variogrammodel'].r['dist'][0])): #@FIXME: so less Pythonic!!
  625. ## dist = Variogram['datavariogram'].r['dist'][0][n]
  626. ## gamma = Variogram['datavariogram'].r['gamma'][0][n]
  627. ## LineList.append((dist,gamma))
  628. ##
  629. ### give them to plot.PolyPoints and plot.PolyLine
  630. ## plot it
  631. #VariogramPlot = plot.PlotGraphics([DataPoints])#,
  632. # #title = "Wa",
  633. # #xlabel = "Distance",
  634. # #ylabel = "Gamma") #([DataPoints, line], title, xlabel, ylabel)
  635. #self.PlotArea.Draw(VariogramPlot)
  636. # B: use R plot function, in a separate window.
  637. thread.start_new_thread(self.plot, ())
  638. #self.plot()
  639. def plot(self):
  640. #robjects.r.X11()
  641. #robjects.r.png("variogram.png")
  642. textplot = robjects.r.plot(Variogram['datavariogram'], Variogram['variogrammodel'])
  643. print textplot
  644. self.refresh()
  645. #robjects.r['dev.off']()
  646. def refresh(self):
  647. while True:
  648. rinterface.process_revents()
  649. time.sleep(0.1)
  650. class RBookgeoRPanel(RBookPanel):
  651. """ Subclass of RBookPanel, with specific geoR options and kriging functions. """
  652. def __init__(self, parent, *args, **kwargs):
  653. RBookPanel.__init__(self, parent, *args, **kwargs)
  654. #@TODO: change these two lines as soon as geoR f(x)s are integrated.
  655. for n in self.GetChildren():
  656. n.Hide()
  657. self.Sizer.Add(wx.StaticText(self, id = wx.ID_ANY, label = _("Work in progress! No functionality provided.")))
  658. self.SetSizerAndFit(self.Sizer)
  659. def main(argv = None):
  660. #@FIXME: solve this double ifelse. the control should not be done twice.
  661. if argv is None:
  662. importR()
  663. argv = sys.argv[1:] #stripping first item, the full name of this script
  664. # wxGUI call.
  665. app = wx.App()
  666. KrigingFrame = KrigingModule(parent = None)
  667. KrigingFrame.Centre()
  668. KrigingFrame.Show()
  669. app.MainLoop()
  670. else:
  671. #CLI
  672. options, flags = argv
  673. #@TODO: Work on verbosity. Sometimes it's too verbose (R), sometimes not enough.
  674. if grass.find_file(options['input'], element = 'vector')['fullname'] is '':
  675. grass.fatal(_("option: <input>: Vector map not found."))
  676. #@TODO: elaborate input string, if contains mapset or not.. thanks again to Bob for testing on 64bit.
  677. # create output map name, if not specified
  678. if options['output'] is '':
  679. try: # to strip mapset name from fullname. Ugh.
  680. options['input'] = options['input'].split("@")[0]
  681. except:
  682. pass
  683. options['output'] = options['input'] + '_kriging'
  684. # check for output map with same name. g.parser can't handle this, afaik.
  685. if grass.find_file(options['output'], element = 'cell')['fullname'] and os.getenv("GRASS_OVERWRITE") == None:
  686. grass.fatal(_("option: <output>: Raster map already exists."))
  687. if options['output_var'] is not '' and (grass.find_file(options['output_var'], element = 'cell')['fullname'] and os.getenv("GRASS_OVERWRITE") == None):
  688. grass.fatal(_("option: <output>: Variance raster map already exists."))
  689. importR()
  690. if options['model'] is '':
  691. try:
  692. robjects.r.require("automap")
  693. except ImportError, e:
  694. grass.fatal(_("R package automap is missing, no variogram autofit available."))
  695. else:
  696. if options['sill'] is '' or options['nugget'] is '' or options['range'] is '':
  697. grass.fatal(_("You have specified model, but forgot at least one of sill, nugget and range."))
  698. #@TODO: let GRASS remount its commandstring. Until then, keep that 4 lines below.
  699. #print grass.write_command(argv)
  700. command = ""
  701. notnulloptions = {}
  702. for k, v in options.items():
  703. if v is not '':
  704. notnulloptions[k] = v
  705. command = command.join("%s=%s " % (k, v) for k, v in notnulloptions.items())
  706. #print command
  707. # re-cast integers from strings, as parser() cast everything to string.
  708. for each in ("sill","nugget","range"):
  709. if options[each] is not '':
  710. options[each] = int(options[each])
  711. else:
  712. options[each] = robjects.r('''NA''')
  713. controller = Controller()
  714. controller.Run(input = options['input'],
  715. column = options['column'],
  716. output = options['output'],
  717. overwrite = os.getenv("GRASS_OVERWRITE") == 1,
  718. package = options['package'],
  719. model = options['model'],
  720. block = options['block'],
  721. sill = options['sill'],
  722. nugget = options['nugget'],
  723. range = options['range'],
  724. output_var = options['output_var'],
  725. command = command,
  726. logger = grass)
  727. def importR():
  728. # R
  729. # unuseful since rpy2 will complain adequately.
  730. # try:
  731. # #@FIXME: in Windows, it launches R terminal
  732. # grass.find_program('R')
  733. # except:
  734. # sys.exit(_("R is not installed. Install it and re-run, or modify environment variables."))
  735. # rpy2
  736. global robjects
  737. global rinterface
  738. grass.message(_('Loading packages, please wait...'))
  739. try:
  740. import rpy2.robjects as robjects
  741. import rpy2.rinterface as rinterface #to speed up kriging? for plots.
  742. haveRpy2 = True
  743. except ImportError:
  744. print >> sys.stderr, _("Rpy2 not found. Please install it and re-run.") # ok for other OSes?
  745. haveRpy2 = False
  746. if not haveRpy2:
  747. sys.exit(1)
  748. # R packages check.
  749. # @FIXME: it leaves a Rtmpxxxx folder into the make tempfolder and causes make complain. [markus]
  750. for each in ["gstat", "spgrass6", "maptools"]:
  751. if not robjects.r.require(each, quietly = True)[0]:
  752. sys.exit(_("R package % is missing. Install it and re-run v.krige.") % each)
  753. if __name__ == '__main__':
  754. if len(sys.argv) > 1:
  755. sys.exit(main(argv = grass.parser()))
  756. else:
  757. main()