vnet_core.py 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110
  1. """!
  2. @package vnet.vnet_core
  3. @brief Vector network analysis logic.
  4. Classes:
  5. - vnet_core::VNETManager
  6. - vnet_core::VNETAnalyses
  7. - vnet_core::VNETHistory
  8. - vnet_core::SnappingNodes
  9. (C) 2013 by the GRASS Development Team
  10. This program is free software under the GNU General Public License
  11. (>=v2). Read the file COPYING that comes with GRASS for details.
  12. @author Stepan Turek <stepan.turek seznam.cz> (GSoC 2012, mentor: Martin Landa)
  13. @author Lukas Bocan <silent_bob centrum.cz> (turn costs support)
  14. @author Eliska Kyzlikova <eliska.kyzlikova gmail.com> (turn costs support)
  15. """
  16. import os
  17. from grass.script import core as grass
  18. import wx
  19. from core import utils
  20. from core.gcmd import RunCommand, GMessage
  21. from core.gconsole import CmdThread, EVT_CMD_DONE, GConsole
  22. from gui_core.gselect import VectorDBInfo
  23. from vnet.vnet_data import VNETData, VNETTmpVectMaps, VectMap, History
  24. from vnet.vnet_utils import ParseMapStr, haveCtypes, GetNearestNodeCat
  25. from grass.pydispatch.signal import Signal
  26. class VNETManager:
  27. def __init__(self, guiparent, giface):
  28. self.data = {}
  29. self.guiparent = guiparent
  30. self.giface = giface
  31. self.mapWin = giface.GetMapWindow()
  32. self.goutput = GConsole(guiparent = guiparent)
  33. self.vnet_data = VNETData(guiparent = guiparent, mapWin = self.mapWin)
  34. self.results = {"analysis" : None,
  35. "vect_map" : None} #TODO more results
  36. # this class instance manages all temporary vector maps created during life of VNETDialog
  37. self.tmp_maps = VNETTmpVectMaps(parent = guiparent, mapWin = self.mapWin)
  38. # initialization of History class used for saving and reading data from file
  39. # it is used for browsing analysis results
  40. self.history = VNETHistory(self.guiparent, self.vnet_data, self.tmp_maps)
  41. self.analyses = VNETAnalyses(self.vnet_data, self.RunAnDone, self.goutput, self.tmp_maps)
  42. self.snap_nodes = SnappingNodes(self.giface, self.vnet_data, self.tmp_maps, self.mapWin)
  43. self.ttbCreated = Signal('VNETManager.ttbCreated')
  44. self.analysisDone = Signal('VNETManager.analysisDone')
  45. self.pointsChanged = self.vnet_data.pointsChanged
  46. self.parametersChanged = self.vnet_data.parametersChanged
  47. self.snapping = self.snap_nodes.snapping
  48. self.pointsChanged.connect(self.PointsChanged)
  49. def __del__(self):
  50. self.CleanUp()
  51. def CleanUp(self):
  52. """!Removes temp layers, unregisters handlers and graphics"""
  53. update = self.tmp_maps.DeleteAllTmpMaps()
  54. self.vnet_data.CleanUp()
  55. if update:
  56. self.giface.updateMap.emit(render=True, renderVector=True)
  57. else:
  58. self.giface.updateMap.emit(render=False, renderVector=False)
  59. def GetPointsManager(self):
  60. return self.vnet_data.GetPointsData()
  61. def GetGlobalTurnsData(self):
  62. return self.vnet_data.GetGlobalTurnsData()
  63. def RunAnalysis(self):
  64. analysis, valid = self.vnet_data.GetParam("analysis")
  65. params, err_params, flags = self.vnet_data.GetParams()
  66. relevant_params = self.vnet_data.GetRelevantParams(analysis)
  67. if not relevant_params:
  68. return -1
  69. if not self.vnet_data.InputsErrorMsgs(_("Unable to perform analysis."),
  70. analysis, params, flags, err_params, relevant_params):
  71. return -2
  72. if self.results["vect_map"]:
  73. self.results["vect_map"].DeleteRenderLayer()
  74. # history - delete data in buffer for hist step
  75. self.history.DeleteNewHistStepData()
  76. # create new map (included to history) for result of analysis
  77. self.results["vect_map"] = self.history.NewTmpVectMapToHist('vnet_tmp_result')
  78. if not self.results["vect_map"]:
  79. return False
  80. # for case there is some map with same name
  81. # (when analysis does not produce any map, this map would have been shown as result)
  82. RunCommand('g.remove',
  83. vect = self.results["vect_map"].GetVectMapName())
  84. # save data from
  85. self.history._saveAnInputToHist(analysis, params, flags)
  86. ret = self.analyses.RunAnalysis(self.results["vect_map"].GetVectMapName(), params, flags)
  87. if not ret:
  88. return -3
  89. else:
  90. return 1
  91. def RunAnDone(self, cmd, returncode, results):
  92. self.results["analysis"] = cmd[0]
  93. self.results["vect_map"].SaveVectMapState()
  94. cmd, cmd_colors = self.vnet_data.GetLayerStyle()
  95. self.results["vect_map"].AddRenderLayer(cmd, cmd_colors)
  96. self.history.SaveHistStep()
  97. self.analysisDone.emit()
  98. def ShowResult(self, show):
  99. #TODO can be more results e. g. smallest cut
  100. if show:
  101. self._checkResultMapChanged(self.results["vect_map"])
  102. cmd, cmd_colors = self.vnet_data.GetLayerStyle()
  103. self.results["vect_map"].AddRenderLayer(cmd, cmd_colors)
  104. else:
  105. self.results["vect_map"].DeleteRenderLayer()
  106. self.giface.updateMap.emit(render=True, renderVector=True)
  107. def GetAnalysisProperties(self, analysis = None):
  108. return self.vnet_data.GetAnalysisProperties(analysis = analysis)
  109. def GetResults(self):
  110. return self.results["vect_map"]
  111. def Undo(self):
  112. self._updateDataForHistStep(self.history.Undo())
  113. #SetUpdateMap TODO
  114. return self.history.GetHistStep()
  115. def Redo(self):
  116. self._updateDataForHistStep(self.history.Redo())
  117. #SetUpdateMap
  118. return self.history.GetHistStep()
  119. def _updateDataForHistStep(self, data):
  120. if not data:
  121. return
  122. analysis, resultMapName, params, flags = data
  123. self.results["analysis"] = analysis
  124. self.vnet_data.SetParams(params, flags)
  125. self.results["vect_map"].DeleteRenderLayer()
  126. self.results["vect_map"] = self.tmp_maps.GetTmpVectMap(resultMapName)
  127. self._checkResultMapChanged(self.results["vect_map"])
  128. cmd, cmd_colors = self.vnet_data.GetLayerStyle()
  129. self.results["vect_map"].AddRenderLayer(cmd, cmd_colors)
  130. self.giface.updateMap.emit(render=True, renderVector=True)
  131. def GetHistStep(self):
  132. return self.history.GetHistStep()
  133. def SetParams(self, params, flags):
  134. self.vnet_data.SetParams(params, flags)
  135. def GetParams(self):
  136. params, inv_params, flags = self.vnet_data.GetParams()
  137. return params, inv_params, flags
  138. def GetParam(self, param):
  139. return self.vnet_data.GetParam(param)
  140. def _checkResultMapChanged(self, resultVectMap):
  141. """!Check if map was modified outside"""
  142. if resultVectMap.VectMapState() == 0:
  143. dlg = wx.MessageDialog(parent = self,
  144. message = _("Temporary map '%s' with result " +
  145. "was changed outside vector network analysis tool.\n" +
  146. "Showed result may not correspond " +
  147. "original analysis result.") %\
  148. resultVectMap.GetVectMapName(),
  149. caption = _("Result changed outside"),
  150. style = wx.ICON_INFORMATION| wx.CENTRE)
  151. dlg.ShowModal()
  152. dlg.Destroy()
  153. def IsSnappingActive(self):
  154. return self.vnet_data.GetSnapping()
  155. def Snapping(self, activate):
  156. self.snap_nodes.ComputeNodes(activate)
  157. def GetAnalyses(self):
  158. return self.vnet_data.GetAnalyses()
  159. def SettingsUpdated(self):
  160. self.vnet_data.GetPointsData().SetPointDrawSettings()
  161. if not self.results["vect_map"] or \
  162. not self.tmp_maps.HasTmpVectMap(self.results["vect_map"].GetVectMapName()):
  163. self.giface.updateMap.emit(render=False, renderVector=False)
  164. elif self.results["vect_map"].GetRenderLayer():
  165. cmd, cmd_colors = self.vnet_data.GetLayerStyle()
  166. self.results["vect_map"].AddRenderLayer(cmd, cmd_colors)
  167. self.giface.updateMap.emit(render=True, renderVector=True)
  168. #TODO optimization
  169. else:
  170. self.giface.updateMap.emit(render=False, renderVector=False)
  171. def PointsChanged(self, method, kwargs):
  172. self.giface.updateMap.emit(render=False, renderVector=False)
  173. def CreateTttb(self, params):
  174. outputMap = params["output"]
  175. mapName, mapSet = ParseMapStr(outputMap)
  176. if mapSet != grass.gisenv()['MAPSET']:
  177. GMessage(parent = self,
  178. message = _("Map can be created only in current mapset"))
  179. return False
  180. existsMap = grass.find_file(name = mapName,
  181. element = 'vector',
  182. mapset = grass.gisenv()['MAPSET'])
  183. if existsMap["name"]:
  184. dlg = wx.MessageDialog(parent = self.guiparent,
  185. message = _("Vector map %s already exists. " +
  186. "Do you want to overwrite it?") %
  187. (existsMap["fullname"]),
  188. caption = _("Overwrite vector map"),
  189. style = wx.YES_NO | wx.NO_DEFAULT |
  190. wx.ICON_QUESTION | wx.CENTRE)
  191. ret = dlg.ShowModal()
  192. dlg.Destroy()
  193. if ret == wx.ID_NO:
  194. return False
  195. cmdTtb = [
  196. "v.net.turntable",
  197. "input=" + params["input"],
  198. "output=" + params["output"],
  199. "alayer=" + params["alayer"],
  200. "tlayer=" + params["tlayer"],
  201. "tuclayer=" + params["tuclayer"],
  202. "--overwrite",
  203. ]
  204. self.goutput.RunCmd(command = cmdTtb, onDone = self._createTtbDone)
  205. return True
  206. def _createTtbDone(self, cmd, returncode):
  207. if returncode != 0:
  208. GMessage(parent = self.guiparent,
  209. message = _("Creation of turntable failed."))
  210. return
  211. else:
  212. params = {}
  213. for c in cmd:
  214. spl_c = c.split("=")
  215. if len(spl_c) != 2:
  216. continue
  217. if spl_c[0] and spl_c != "input":
  218. params[spl_c[0]] = spl_c[1]
  219. if spl_c[0] == "output":
  220. params["input"] = spl_c[1]
  221. self.vnet_data.SetParams(params, {})
  222. self.ttbCreated.emit(returncode = returncode)
  223. def SaveTmpLayer(self, layer_name):
  224. """!Permanently saves temporary map of analysis result"""
  225. msg = _("Vector map with analysis result does not exist.")
  226. if not hasattr(self.results["vect_map"], "GetVectMapName"):
  227. GMessage(parent = self.guiparent,
  228. message = msg)
  229. return
  230. mapToAdd = self.results["vect_map"].GetVectMapName()
  231. mapToAddEx = grass.find_file(name = mapToAdd,
  232. element = 'vector',
  233. mapset = grass.gisenv()['MAPSET'])
  234. if not mapToAddEx["name"]:
  235. GMessage(parent = self.guiparent,
  236. message = msg)
  237. return
  238. addedMap = layer_name
  239. mapName, mapSet = ParseMapStr(addedMap)
  240. if mapSet != grass.gisenv()['MAPSET']:
  241. GMessage(parent = self.guiparent,
  242. message = _("Map can be saved only to currently set mapset"))
  243. return
  244. existsMap = grass.find_file(name = mapName,
  245. element = 'vector',
  246. mapset = grass.gisenv()['MAPSET'])
  247. if existsMap["name"]:
  248. dlg = wx.MessageDialog(parent = self.guiparent,
  249. message = _("Vector map %s already exists. " +
  250. "Do you want to overwrite it?") %
  251. (existsMap["fullname"]),
  252. caption = _("Overwrite vector map"),
  253. style = wx.YES_NO | wx.NO_DEFAULT |
  254. wx.ICON_QUESTION | wx.CENTRE)
  255. ret = dlg.ShowModal()
  256. if ret == wx.ID_NO:
  257. dlg.Destroy()
  258. return
  259. dlg.Destroy()
  260. RunCommand("g.copy",
  261. overwrite = True,
  262. vect = [self.results["vect_map"].GetVectMapName(), mapName])
  263. if not self.mapWin.tree:
  264. return
  265. if self.mapWin.tree.FindItemByData(key = 'name', value = mapName) is None:
  266. cmd, cmd_colors = self.vnet_data.GetLayerStyle()#TODO get rid of insert
  267. cmd.insert(0, 'd.vect')
  268. cmd.append('map=%s' % mapName)
  269. self.mapWin.tree.AddLayer(ltype = "vector",
  270. lname = mapName,
  271. lcmd = cmd,
  272. lchecked = True)
  273. if cmd_colors:
  274. layerStyleVnetColors = utils.CmdToTuple(cmd_colors)
  275. RunCommand(layerStyleVnetColors[0],
  276. **layerStyleVnetColors[1])
  277. else:
  278. self.giface.updateMap.emit(render=True, renderVector=True)
  279. class VNETAnalyses:
  280. def __init__(self, data, onAnDone, goutput, tmp_maps):
  281. self.data = data
  282. self.pts_data = self.data.GetPointsData()
  283. self.tmp_maps = tmp_maps
  284. self.onAnDone = onAnDone
  285. self.goutput = goutput
  286. def RunAnalysis(self, output, params, flags):
  287. """!Perform network analysis"""
  288. analysis, valid = self.data.GetParam("analysis")
  289. catPts = self._getPtByCat(analysis)
  290. if analysis == "v.net.path":
  291. self._vnetPathRunAn(analysis, output, params, flags, catPts)
  292. elif flags["t"]:
  293. self._runTurnsAn(analysis, output, params, flags, catPts)
  294. else:
  295. self._runAn(analysis, output, params, flags, catPts)
  296. def _vnetPathRunAn(self, analysis, output, params, flags, catPts):
  297. """!Called when analysis is run for v.net.path module"""
  298. if self.pts_data.GetPointsCount() < 1:
  299. return False
  300. cats = self.data.GetAnalysisProperties()["cmdParams"]["cats"]
  301. # Creates part of cmd fro analysis
  302. cmdParams = [analysis]
  303. cmdParams.extend(self._setInputParams(analysis, params, flags))
  304. cmdParams.append("output=" + output)
  305. cmdPts = []
  306. for cat in cats:
  307. if len(catPts[cat[0]]) < 1:#TODO
  308. GMessage(message=_("Please choose '%s' and '%s' point.") % (cats[0][1], cats[1][1]))
  309. return False
  310. cmdPts.append(catPts[cat[0]][0])
  311. resId = 1
  312. inpPoints = str(resId) + " " + str(cmdPts[0][0]) + " " + str(cmdPts[0][1]) + \
  313. " " + str(cmdPts[1][0]) + " " + str(cmdPts[1][1])
  314. self.coordsTmpFile = grass.tempfile()
  315. coordsTmpFileOpened = open(self.coordsTmpFile, 'w')
  316. coordsTmpFileOpened.write(inpPoints)
  317. coordsTmpFileOpened.close()
  318. if flags["t"]:
  319. cmdParams.append("-t")
  320. self.tmpTurnAn = AddTmpMapAnalysisMsg("vnet_tunr_an_tmp", self.tmp_maps)
  321. if not self.tmpTurnAn:
  322. return False
  323. mapName, mapSet = ParseMapStr(self.tmpTurnAn.GetVectMapName())
  324. cmdCopy = [
  325. "g.copy",
  326. "vect=%s,%s" % (params["input"], mapName),
  327. "--overwrite",
  328. ]
  329. cmdParams.append("input=" + self.tmpTurnAn.GetVectMapName())
  330. ret, msg, err = RunCommand('g.copy',
  331. getErrorMsg = True,
  332. vect = "%s,%s" % (params['input'], mapName),
  333. read = True,
  334. overwrite = True)
  335. self._updateTtbByGlobalCosts(self.tmpTurnAn.GetVectMapName(),
  336. int(params["tlayer"]))
  337. #self._prepareCmd(cmdCopy)
  338. #self.goutput.RunCmd(command = cmdCopy)
  339. else:
  340. cmdParams.append("input=" + params["input"])
  341. cmdParams.append("file=" + self.coordsTmpFile)
  342. cmdParams.append("dmax=" + str(params["max_dist"]))
  343. cmdParams.append("--overwrite")
  344. self._prepareCmd(cmd = cmdParams)
  345. if flags["t"]:
  346. self.goutput.RunCmd(command = cmdParams, onDone = self._vnetPathRunTurnsAnDone)
  347. else:
  348. self.goutput.RunCmd(command = cmdParams, onDone = self._vnetPathRunAnDone)
  349. def _vnetPathRunTurnsAnDone(self, cmd, returncode):
  350. #TODO
  351. #self.tmp_maps.DeleteTmpMap(self.tmpTurnAn)
  352. self._vnetPathRunAnDone(cmd, returncode)
  353. def _vnetPathRunAnDone(self, cmd, returncode):
  354. """!Called when v.net.path analysis is done"""
  355. grass.try_remove(self.coordsTmpFile)
  356. self._onDone(cmd, returncode)
  357. def _onDone(self, cmd, returncode):
  358. for c in cmd:
  359. if "output=" in c:
  360. output = c.split("=")[1]
  361. break
  362. self.onAnDone(cmd, returncode, output)
  363. def _runTurnsAn(self, analysis, output, params, flags, catPts):
  364. # Creates part of cmd fro analysis
  365. cmdParams = [analysis]
  366. cmdParams.extend(self._setInputParams(analysis, params, flags))
  367. cmdParams.append("output=" + output)
  368. cats = {}
  369. for cat_name, pts_coor in catPts.iteritems():
  370. for coor in pts_coor:
  371. cat_num = str(GetNearestNodeCat(e = coor[0],
  372. n = coor[1],
  373. field = int(params["tuclayer"],
  374. tresh = params["max_dist"]),
  375. vectMap = params["input"]))
  376. if cat_num < 0:
  377. continue
  378. if cats.has_key(cat_name):
  379. cats[cat_name].append(cat_num)
  380. else:
  381. cats[cat_name] = [cat_num]
  382. for cat_name, cat_nums in cats.iteritems():
  383. cmdParams.append(cat_name + "=" + ",".join(cat_nums))
  384. self.tmpTurnAn = AddTmpMapAnalysisMsg("vnet_tunr_an_tmp", self.tmp_maps)
  385. if not self.tmpTurnAn:
  386. return False
  387. # create and run commands which goes to analysis thread
  388. mapName, mapSet = ParseMapStr(self.tmpTurnAn.GetVectMapName())
  389. cmdCopy = [
  390. "g.copy",
  391. "vect=%s,%s" % (params['input'], mapName),
  392. "--overwrite",
  393. ]
  394. cmdParams.append("input=" + self.tmpTurnAn.GetVectMapName())
  395. ret, msg, err = RunCommand('g.copy',
  396. getErrorMsg = True,
  397. vect = "%s,%s" % (params['input'], mapName),
  398. read = True,
  399. overwrite = True)
  400. self._updateTtbByGlobalCosts(self.tmpTurnAn.GetVectMapName(),
  401. int(params["tlayer"]))
  402. self._setCmdForSpecificAn(cmdParams)
  403. cmdParams.append("-t")
  404. self._prepareCmd(cmdParams)
  405. self.goutput.RunCmd(command = cmdParams, onDone = self._runTurnsAnDone)
  406. def _updateTtbByGlobalCosts(self, vectMapName, tlayer):
  407. #TODO get layer number do not use it directly
  408. intervals = self.turnsData["global"].GetData()
  409. cmdUpdGlob = [
  410. "v.db.update",
  411. "map=", self.inputData["input"].GetValue(),
  412. "layer=%d" % tlayer,
  413. "column=cost",
  414. ]
  415. dbInfo = VectorDBInfo(vectMapName)
  416. table = dbInfo.GetTable(tlayer)
  417. driver, database = dbInfo.GetDbSettings(tlayer)
  418. sqlFile = grass.tempfile()
  419. sqlFile_f = open(sqlFile, 'w')
  420. for ival in intervals:
  421. from_angle = ival[0]
  422. to_angle = ival[1]
  423. cost = ival[2]
  424. if to_angle < from_angle:
  425. to_angle = math.pi * 2 + to_angle
  426. #if angle < from_angle:
  427. # angle = math.pi * 2 + angle
  428. where = " WHERE (((angle < {0}) AND ({2} + angle >= {0} AND {2} + angle < {1})) OR \
  429. ((angle >= {0}) AND (angle >= {0} AND angle < {1}))) AND cost==0.0 ".format(str(from_angle), str(to_angle), str(math.pi * 2))
  430. stm = ("UPDATE %s SET cost=%f " % (table, cost)) + where + ";\n";
  431. sqlFile_f.write(stm)
  432. sqlFile_f.close()
  433. #TODO imporve parser and run in thread
  434. ret, msg, err = RunCommand('db.execute',
  435. getErrorMsg = True,
  436. input = sqlFile,
  437. read = True,
  438. driver = driver,
  439. database = database)
  440. grass.try_remove(sqlFile)
  441. def _runTurnsAnDone(self, cmd, returncode):
  442. """!Called when analysis is done"""
  443. #self.tmp_maps.DeleteTmpMap(self.tmpTurnAn) #TODO remove earlier (OnDone lambda?)
  444. self._onDone(cmd, returncode)
  445. def _runAn(self, analysis, output, params, flags, catPts):
  446. """!Called for all v.net.* analysis (except v.net.path)"""
  447. # Creates part of cmd fro analysis
  448. cmdParams = [analysis]
  449. cmdParams.extend(self._setInputParams(analysis, params, flags))
  450. cmdParams.append("output=" + output)
  451. cats = self.data.GetAnalysisProperties()["cmdParams"]["cats"]
  452. if len(cats) > 1:
  453. for cat in cats:
  454. if len(catPts[cat[0]]) < 1:
  455. GMessage(parent = self,
  456. message = _("Please choose '%s' and '%s' point.") \
  457. % (cats[0][1], cats[1][1]))
  458. return False
  459. else:
  460. for cat in cats:
  461. if len(catPts[cat[0]]) < 2:
  462. GMessage(parent = self,
  463. message = _("Please choose at least two points."))
  464. return False
  465. # TODO add also to thread for analysis?
  466. vcatResult = RunCommand("v.category",
  467. input = params['input'],
  468. option = "report",
  469. flags = "g",
  470. read = True)
  471. vcatResult = vcatResult.splitlines()
  472. for cat in vcatResult:#TODO
  473. cat = cat.split()
  474. if "all" in cat:
  475. maxCat = int(cat[4])
  476. break
  477. layerNum = params["nlayer"]
  478. pt_ascii, catsNums = self._getAsciiPts (catPts = catPts,
  479. maxCat = maxCat,
  480. layerNum = layerNum)
  481. self.tmpPtsAsciiFile = grass.tempfile()#TODO better tmp files cleanup (make class for managing tmp files)
  482. tmpPtsAsciiFileOpened = open(self.tmpPtsAsciiFile, 'w')
  483. tmpPtsAsciiFileOpened.write(pt_ascii)
  484. tmpPtsAsciiFileOpened.close()
  485. self.tmpInPts = AddTmpMapAnalysisMsg("vnet_tmp_in_pts", self.tmp_maps)
  486. if not self.tmpInPts:
  487. return False
  488. self.tmpInPtsConnected = AddTmpMapAnalysisMsg("vnet_tmp_in_pts_connected", self.tmp_maps)
  489. if not self.tmpInPtsConnected:
  490. return False
  491. cmdParams.append("input=" + self.tmpInPtsConnected.GetVectMapName())
  492. cmdParams.append("--overwrite")
  493. self._setCmdForSpecificAn(cmdParams)
  494. for catName, catNum in catsNums.iteritems():
  495. if catNum[0] == catNum[1]:
  496. cmdParams.append(catName + "=" + str(catNum[0]))
  497. else:
  498. cmdParams.append(catName + "=" + str(catNum[0]) + "-" + str(catNum[1]))
  499. # create and run commands which goes to analysis thread
  500. cmdVEdit = [
  501. "v.edit",
  502. "map=" + self.tmpInPts.GetVectMapName(),
  503. "input=" + self.tmpPtsAsciiFile,
  504. "tool=create",
  505. "--overwrite",
  506. "-n"
  507. ]
  508. self._prepareCmd(cmdVEdit)
  509. self.goutput.RunCmd(command = cmdVEdit)
  510. cmdVNet = [
  511. "v.net",
  512. "points=" + self.tmpInPts.GetVectMapName(),
  513. "input=" + params['input'],
  514. "output=" + self.tmpInPtsConnected.GetVectMapName(),
  515. "alayer=" + params["alayer"],
  516. "nlayer=" + params["nlayer"],
  517. "operation=connect",
  518. "thresh=" + str(params["max_dist"]),
  519. "--overwrite"
  520. ] #TODO snapping to nodes optimization
  521. self._prepareCmd(cmdVNet)
  522. self.goutput.RunCmd(command = cmdVNet)
  523. self._prepareCmd(cmdParams)
  524. self.goutput.RunCmd(command = cmdParams, onDone = self._runAnDone)
  525. def _runAnDone(self, cmd, returncode):
  526. """!Called when analysis is done"""
  527. self.tmp_maps.DeleteTmpMap(self.tmpInPts) #TODO remove earlier (OnDone lambda?)
  528. self.tmp_maps.DeleteTmpMap(self.tmpInPtsConnected)
  529. grass.try_remove(self.tmpPtsAsciiFile)
  530. if cmd[0] == "v.net.flow":
  531. self.tmp_maps.DeleteTmpMap(self.vnetFlowTmpCut)
  532. self._onDone(cmd, returncode)
  533. def _setInputParams(self, analysis, params, flags):
  534. """!Return list of chosen values (vector map, layers).
  535. The list items are in form to be used in command for analysis e.g. 'alayer=1'.
  536. """
  537. inParams = []
  538. for col, v in self.data.GetAnalysisProperties()["cmdParams"]["cols"].iteritems():
  539. if "inputField" in v:
  540. colInptF = v["inputField"]
  541. else:
  542. colInptF = col
  543. inParams.append(col + '=' + params[colInptF])
  544. for layer in ['alayer', 'nlayer', 'tlayer', 'tuclayer']:
  545. if not flags["t"] and layer in ['tlayer', 'tuclayer']:
  546. continue
  547. # TODO
  548. if flags["t"] and layer == 'nlayer':
  549. inParams.append(layer + "=" + params['tuclayer'])
  550. continue
  551. inParams.append(layer + "=" + params[layer])
  552. return inParams
  553. def _getPtByCat(self, analysis):
  554. """!Return points separated by theirs categories"""
  555. anProps = self.data.GetAnalysisProperties()
  556. cats = anProps["cmdParams"]["cats"]
  557. ptByCats = {}
  558. for cat in anProps["cmdParams"]["cats"]:
  559. ptByCats[cat[0]] = []
  560. for i in range(self.pts_data.GetPointsCount()):
  561. pt_data = self.pts_data.GetPointData(i)
  562. if pt_data["use"]:
  563. for i_cat, cat in enumerate(cats):
  564. #i_cat + 1 - we ave to increment it because pt_data["type"] includes "" in first place
  565. if (i_cat + 1) == pt_data["type"] or len(ptByCats) == 1:
  566. coords = (pt_data['e'], pt_data['n'])
  567. ptByCats[cat[0]].append(coords)
  568. return ptByCats
  569. def _getAsciiPts (self, catPts, maxCat, layerNum):
  570. """!Return points separated by categories in GRASS ASCII vector representation"""
  571. catsNums = {}
  572. pt_ascii = ""
  573. catNum = maxCat
  574. for catName, pts in catPts.iteritems():
  575. catsNums[catName] = [catNum + 1]
  576. for pt in pts:
  577. catNum += 1
  578. pt_ascii += "P 1 1\n"
  579. pt_ascii += str(pt[0]) + " " + str(pt[1]) + "\n"
  580. pt_ascii += str(layerNum) + " " + str(catNum) + "\n"
  581. catsNums[catName].append(catNum)
  582. return pt_ascii, catsNums
  583. def _prepareCmd(self, cmd):
  584. """!Helper function for preparation of cmd list into form for RunCmd method"""
  585. for c in cmd[:]:
  586. if c.find("=") == -1:
  587. continue
  588. v = c.split("=")
  589. if len(v) != 2:
  590. cmd.remove(c)
  591. elif not v[1].strip():
  592. cmd.remove(c)
  593. def _setCmdForSpecificAn(self, cmdParams):
  594. # append parameters needed for particular analysis
  595. if cmdParams[0] == "v.net.distance":
  596. cmdParams.append("from_layer=1")
  597. cmdParams.append("to_layer=1")
  598. elif cmdParams[0] == "v.net.flow":
  599. #self.vnetFlowTmpCut = self.NewTmpVectMapToHist('vnet_tmp_flow_cut') TODO
  600. self.vnetFlowTmpCut = AddTmpMapAnalysisMsg('vnet_tmp_flow_cut', self.tmp_maps)
  601. if not self.vnetFlowTmpCut:
  602. return
  603. cmdParams.append("cut=" + self.vnetFlowTmpCut.GetVectMapName())
  604. elif cmdParams[0] == "v.net.iso":
  605. costs, valid = self.data.GetParam("iso_lines") #TODO valid
  606. cmdParams.append("costs=" + costs)
  607. class VNETHistory():
  608. def __init__(self, guiparent, data, tmp_maps):
  609. self.history = History()
  610. self.guiparent = guiparent
  611. self.tmp_maps = tmp_maps
  612. # variable, which appends unique number to every name of map, which is saved into history
  613. self.histTmpVectMapNum = 0
  614. self.data = data
  615. def Undo(self):
  616. """!Step back in history"""
  617. histStepData = self.history.GetPrev()
  618. if histStepData:
  619. return self._updateHistStepData(histStepData)
  620. return None
  621. def Redo(self):
  622. """!Step forward in history"""
  623. histStepData = self.history.GetNext()
  624. if histStepData:
  625. return self._updateHistStepData(histStepData)
  626. return None
  627. def GetHistStep(self):
  628. return self.history.GetCurrHistStep(), self.history.GetStepsNum()
  629. def SaveHistStep(self):
  630. """!Save new step into history"""
  631. removedHistData = self.history.SaveHistStep()
  632. if not removedHistData:
  633. return
  634. # delete temporary maps in history steps which were deleted
  635. for removedStep in removedHistData.itervalues():
  636. mapsNames = removedStep["tmp_data"]["maps"]
  637. for vectMapName in mapsNames:
  638. tmpMap = self.tmp_maps.GetTmpVectMap(vectMapName)
  639. self.tmp_maps.DeleteTmpMap(tmpMap)
  640. def DeleteNewHistStepData(self):
  641. # history - delete data in buffer for hist step
  642. self.history.DeleteNewHistStepData()
  643. # empty list for maps to be saved to history
  644. self.tmpVectMapsToHist= []
  645. def _updateHistStepData(self, histStepData):
  646. """!Updates dialog according to chosen history step"""
  647. # set analysis module
  648. analysis = histStepData["vnet_modules"]["curr_module"]
  649. self.data.SetParams({"analysis" : analysis}, {})
  650. pts = []
  651. # add points to list
  652. for iPt in range(len(histStepData["points"])):
  653. ptDataHist = histStepData["points"]["pt" + str(iPt)]
  654. e, n = ptDataHist["coords"]
  655. pt_data = {"e" : e, "n" : n}
  656. pt_data['type'] = int(ptDataHist["catIdx"])
  657. pt_data['topology'] = ptDataHist["topology"]
  658. pt_data['use'] = ptDataHist["checked"]
  659. pts.append(pt_data)
  660. self.data.GetPointsData().SetPoints(pts)
  661. # update analysis result maps
  662. mapsNames = histStepData["tmp_data"]["maps"]
  663. for m in mapsNames:
  664. if "vnet_tmp_result" in m:
  665. resultMapName = m
  666. break
  667. # update parameters
  668. params = {}
  669. histInputData = histStepData["an_params"]
  670. for inpName, inp in histInputData.iteritems():
  671. params[inpName] = str(inp)
  672. if inpName == "input":
  673. inpMap = inp
  674. prevInpModTime = str(histStepData["other"]["input_modified"])
  675. currInpModTime = VectMap(None, inpMap).GetLastModified()
  676. if currInpModTime.strip()!= prevInpModTime.strip():
  677. dlg = wx.MessageDialog(parent = self.guiparent,
  678. message = _("Input map '%s' for analysis was changed outside " +
  679. "vector network analysis tool.\n" +
  680. "Topology column may not " +
  681. "correspond to changed situation.") %\
  682. inpMap,
  683. caption = _("Input changed outside"),
  684. style = wx.ICON_INFORMATION| wx.CENTRE)
  685. dlg.ShowModal()
  686. dlg.Destroy()
  687. #TODO
  688. flags = {}
  689. return analysis, resultMapName, params, flags
  690. def _saveAnInputToHist(self, analysis, params, flags):
  691. """!Save all data needed for analysis into history buffer"""
  692. pts_num = self.data.GetPointsData().GetPointsCount()
  693. for pt_id in range(pts_num):
  694. data = self.data.GetPointsData().GetPointData(pt_id)
  695. ptName = "pt" + str(pt_id)
  696. coords = [data["e"], data["n"]]
  697. self.history.Add(key = "points",
  698. subkey = [ptName, "coords"],
  699. value = coords)
  700. self.history.Add(key = "points",
  701. subkey = [ptName, "catIdx"],
  702. value = data['type'])
  703. self.history.Add(key = "points",
  704. subkey = [ptName, "topology"],
  705. value = data['topology'])
  706. self.history.Add(key = "points",
  707. subkey = [ptName, "checked"],
  708. value = data["use"])
  709. for param, value in params.iteritems():
  710. if param == "input":
  711. inpMap = VectMap(self, value)
  712. self.history.Add(key = "other",
  713. subkey = "input_modified",
  714. value = inpMap.GetLastModified())
  715. param_val = value
  716. else:
  717. param_val = value
  718. self.history.Add(key = "an_params",
  719. subkey = param,
  720. value = param_val)
  721. self.history.Add(key = "vnet_modules",
  722. subkey = "curr_module",
  723. value = analysis)
  724. def NewTmpVectMapToHist(self, prefMapName):
  725. """!Add new vector map, which will be saved into history step"""
  726. mapName = prefMapName + str(self.histTmpVectMapNum)
  727. self.histTmpVectMapNum += 1
  728. tmpMap = AddTmpMapAnalysisMsg(mapName, self.tmp_maps)
  729. if not tmpMap:
  730. return tmpMap
  731. self.tmpVectMapsToHist.append(tmpMap.GetVectMapName())
  732. self.history.Add(key = "tmp_data",
  733. subkey = "maps",
  734. value = self.tmpVectMapsToHist)
  735. return tmpMap
  736. def AddTmpMapAnalysisMsg(mapName, tmp_maps): #TODO
  737. """!Wraped AddTmpVectMap"""
  738. msg = _("Temporary map %s already exists.\n" +
  739. "Do you want to continue in analysis and overwrite it?") \
  740. % (mapName +'@' + grass.gisenv()['MAPSET'])
  741. tmpMap = tmp_maps.AddTmpVectMap(mapName, msg)
  742. return tmpMap
  743. class SnappingNodes(wx.EvtHandler):
  744. def __init__(self, giface, data, tmp_maps, mapWin):
  745. self.giface = giface
  746. self.data = data
  747. self.tmp_maps = tmp_maps
  748. self.mapWin = mapWin
  749. wx.EvtHandler.__init__(self)
  750. self.snapping= Signal('VNETManager.snapping')
  751. # Stores all data related to snapping
  752. self.snapData = {}
  753. def ComputeNodes(self, activate):
  754. """!Start/stop snapping mode"""
  755. if not haveCtypes:
  756. GMessage(parent = self,
  757. message = _("Unable to use ctypes. \n") + \
  758. _("Snapping mode can not be activated."))
  759. return -1
  760. if not activate:
  761. if self.tmp_maps.HasTmpVectMap("vnet_snap_points"):
  762. self.snapPts.DeleteRenderLayer()
  763. self.giface.updateMap.emit(render=False, renderVector=False)
  764. if self.snapData.has_key('cmdThread'):
  765. self.snapData['cmdThread'].abort()
  766. self.data.SetSnapping(False)
  767. self.snapping.emit(evt = "deactivated")
  768. return -1
  769. self.data.SetSnapping(activate)
  770. params, inv_params, flags = self.data.GetParams()
  771. if not self.data.InputsErrorMsgs(msg = _("Snapping mode can not be activated."),
  772. analysis = None,
  773. params = params,
  774. inv_params = inv_params,
  775. flags = flags,
  776. relevant_params = ["input", "nlayer"]):
  777. return -1
  778. if not self.tmp_maps.HasTmpVectMap("vnet_snap_points"):
  779. endStr = _("Do you really want to activate snapping and overwrite it?")
  780. self.snapPts = self.tmp_maps.AddTmpVectMap("vnet_snap_points", endStr)
  781. if not self.snapPts:
  782. return -1
  783. elif self.snapPts.VectMapState() == 0:
  784. dlg = wx.MessageDialog(message = _("Temporary map '%s' was changed outside " +
  785. "vector analysis tool.\n"
  786. "Do you really want to activate " +
  787. "snapping and overwrite it? ") % \
  788. self.snapPts.GetVectMapName(),
  789. caption = _("Overwrite map"),
  790. style = wx.YES_NO | wx.NO_DEFAULT |
  791. wx.ICON_QUESTION | wx.CENTRE)
  792. ret = dlg.ShowModal()
  793. dlg.Destroy()
  794. if ret == wx.ID_NO:
  795. self.tmp_maps.DeleteTmpMap(self.snapPts)
  796. return -1
  797. self.data.SetSnapping(True)
  798. inpFullName = params["input"]
  799. inpName, mapSet = inpFullName.split("@")
  800. computeNodes = True
  801. if not self.snapData.has_key("inputMap"):
  802. pass
  803. elif inpFullName != self.snapData["inputMap"].GetVectMapName():
  804. self.snapData["inputMap"] = VectMap(None, inpFullName)
  805. elif self.snapData["inputMap"].VectMapState() == 1:
  806. computeNodes = False
  807. # new map needed
  808. if computeNodes:
  809. if not self.snapData.has_key('cmdThread'):
  810. self.snapData['cmdThread'] = CmdThread(self)
  811. else:
  812. self.snapData['cmdThread'].abort()
  813. cmd = ["v.to.points", "input=" + params['input'],
  814. "output=" + self.snapPts.GetVectMapName(),
  815. "use=node", "--overwrite"]
  816. # process GRASS command with argument
  817. self.snapData["inputMap"] = VectMap(None, inpFullName)
  818. self.snapData["inputMap"].SaveVectMapState()
  819. self.Bind(EVT_CMD_DONE, self._onNodesDone)
  820. self.snapData['cmdThread'].RunCmd(cmd)
  821. self.snapping.emit(evt = "computing_points")
  822. return 0
  823. # map is already created and up to date for input data
  824. else:
  825. self.snapPts.AddRenderLayer()
  826. self.giface.updateMap.emit(render=True, renderVector=True)
  827. self.snapping.emit(evt = "computing_points_done")
  828. return 1
  829. def _onNodesDone(self, event):
  830. """!Update map window, when map with nodes to snap is created"""
  831. if not event.aborted:
  832. self.snapPts.SaveVectMapState()
  833. self.snapPts.AddRenderLayer()
  834. self.giface.updateMap.emit(render=True, renderVector=True)
  835. self.snapping.emit(evt = "computing_points_done")