vnet_core.py 38 KB

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