vnet_core.py 39 KB

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