vnet_core.py 40 KB

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