vnet_core.py 39 KB

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