vnet_core.py 39 KB

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