vnet_core.py 40 KB

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