vnet_core.py 41 KB

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