vnet_data.py 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564
  1. """
  2. @package vnet.vnet_data
  3. @brief Vector network analysis classes for data managment.
  4. Classes:
  5. - vnet_data::VNETData
  6. - vnet_data::VNETPointsData
  7. - vnet_data::VNETAnalysisParameters
  8. - vnet_data::VNETAnalysesProperties
  9. - vnet_data::VNETTmpVectMaps
  10. - vnet_data::VectMap
  11. - vnet_data::History
  12. - vnet_data::VNETGlobalTurnsData
  13. (C) 2013-2014 by the GRASS Development Team
  14. This program is free software under the GNU General Public License
  15. (>=v2). Read the file COPYING that comes with GRASS for details.
  16. @author Stepan Turek <stepan.turek seznam.cz> (GSoC 2012, mentor: Martin Landa)
  17. @author Lukas Bocan <silent_bob centrum.cz> (turn costs support)
  18. @author Eliska Kyzlikova <eliska.kyzlikova gmail.com> (turn costs support)
  19. """
  20. import os
  21. import types
  22. import six
  23. from copy import deepcopy
  24. from grass.script.utils import try_remove
  25. from grass.script import core as grass
  26. from grass.script.task import cmdlist_to_tuple
  27. import wx
  28. from core import utils
  29. from core.gcmd import RunCommand, GMessage
  30. from core.settings import UserSettings
  31. from core.utils import _
  32. from vnet.vnet_utils import ParseMapStr, SnapToNode
  33. from gui_core.gselect import VectorDBInfo
  34. from grass.pydispatch.signal import Signal
  35. from vnet.vnet_utils import DegreesToRadians, RadiansToDegrees
  36. class VNETData:
  37. def __init__(self, guiparent, mapWin):
  38. # setting initialization
  39. self._initSettings()
  40. self.guiparent = guiparent
  41. self.an_props = VNETAnalysesProperties()
  42. self.an_params = VNETAnalysisParameters(self.an_props)
  43. self.an_points = VNETPointsData(mapWin, self.an_props, self.an_params)
  44. self.global_turns = VNETGlobalTurnsData()
  45. self.pointsChanged = self.an_points.pointsChanged
  46. self.parametersChanged = self.an_params.parametersChanged
  47. def CleanUp(self):
  48. self.an_points.CleanUp()
  49. def GetAnalyses(self):
  50. return self.an_props.used_an
  51. def GetPointsData(self):
  52. return self.an_points
  53. def GetGlobalTurnsData(self):
  54. return self.global_turns
  55. def GetRelevantParams(self, analysis=None):
  56. if analysis:
  57. return self.an_props.GetRelevantParams(analysis)
  58. else:
  59. analysis, valid = self.an_params.GetParam("analysis")
  60. return self.an_props.GetRelevantParams(analysis)
  61. def GetAnalysisProperties(self, analysis=None):
  62. if analysis:
  63. return self.an_props[analysis]
  64. else:
  65. analysis, valid = self.an_params.GetParam("analysis")
  66. return self.an_props[analysis]
  67. def GetParam(self, param):
  68. return self.an_params.GetParam(param)
  69. def GetParams(self):
  70. return self.an_params.GetParams()
  71. def SetParams(self, params, flags):
  72. return self.an_params.SetParams(params, flags)
  73. def SetSnapping(self, activate):
  74. self.an_points.SetSnapping(activate)
  75. def GetSnapping(self):
  76. return self.an_points.GetSnapping()
  77. def GetLayerStyle(self):
  78. """Returns cmd for d.vect, with set style for analysis result"""
  79. analysis, valid = self.an_params.GetParam("analysis")
  80. resProps = self.an_props[analysis]["resultProps"]
  81. width = UserSettings.Get(
  82. group='vnet',
  83. key='res_style',
  84. subkey="line_width")
  85. layerStyleCmd = ["layer=1", 'width=' + str(width)]
  86. if "catColor" in resProps:
  87. layerStyleCmd.append('flags=c')
  88. elif "singleColor" in resProps:
  89. col = UserSettings.Get(
  90. group='vnet',
  91. key='res_style',
  92. subkey="line_color")
  93. layerStyleCmd.append(
  94. 'color=' + str(col[0]) + ':' + str(col[1]) + ':' + str(col[2]))
  95. layerStyleVnetColors = []
  96. if "attrColColor" in resProps:
  97. colorStyle = UserSettings.Get(
  98. group='vnet', key='res_style', subkey="color_table")
  99. invert = UserSettings.Get(
  100. group='vnet',
  101. key='res_style',
  102. subkey="invert_colors")
  103. layerStyleVnetColors = [
  104. "v.colors",
  105. "color=" + colorStyle,
  106. "column=" + resProps["attrColColor"],
  107. ]
  108. if invert:
  109. layerStyleVnetColors.append("-n")
  110. return layerStyleCmd, layerStyleVnetColors
  111. def InputsErrorMsgs(self, msg, analysis, params, flags,
  112. inv_params, relevant_params):
  113. """Checks input data in Parameters tab and shows messages if some value is not valid
  114. :param str msg: message added to start of message string
  115. :return: True if checked inputs are OK
  116. :return: False if some of checked inputs is not ok
  117. """
  118. if flags["t"] and "turn_layer" not in relevant_params:
  119. GMessage(
  120. parent=self.guiparent, message=_(
  121. "Module <%s> does not support turns costs." %
  122. analysis))
  123. return False
  124. errMapStr = ""
  125. if 'input' in inv_params:
  126. if params['input']:
  127. errMapStr = _("Vector map '%s' does not exist.") % (
  128. params['input'])
  129. else:
  130. errMapStr = _("Vector map was not chosen.")
  131. if errMapStr:
  132. GMessage(parent=self.guiparent,
  133. message=msg + "\n" + errMapStr)
  134. return False
  135. errLayerStr = ""
  136. vals = {
  137. 'arc_layer': _("arc layer"),
  138. 'node_layer': _("node layer"),
  139. 'turn_layer': _("turntable layer"),
  140. 'turn_cat_layer': _("unique categories layer")
  141. }
  142. for layer, layerLabel in six.iteritems(vals):
  143. if layer in ["turn_layer", "turn_cat_layer"] and not flags["t"]:
  144. continue
  145. if layer in inv_params:
  146. if params[layer]:
  147. errLayerStr += _("Chosen %s '%s' does not exist in vector map '%s'.\n") % (
  148. layerLabel, params[layer], params['input'])
  149. else:
  150. errLayerStr += _("Choose existing %s.\n") % \
  151. (layerLabel)
  152. if errLayerStr:
  153. GMessage(parent=self.guiparent,
  154. message=msg + "\n" + errLayerStr)
  155. return False
  156. errColStr = ""
  157. for col in ["arc_column", "arc_backward_column", "node_column"]:
  158. if params[col] and col in inv_params and col in relevant_params:
  159. errColStr += _("Chosen column '%s' does not exist in attribute table of layer '%s' of vector map '%s'.\n") % (
  160. params[col], params[layer], params['input'])
  161. if errColStr:
  162. GMessage(parent=self.guiparent,
  163. message=msg + "\n" + errColStr)
  164. return False
  165. return True
  166. def _initSettings(self):
  167. """Initialization of settings (if not already defined)"""
  168. # initializes default settings
  169. initSettings = [
  170. ['res_style', 'line_width', 5],
  171. ['res_style', 'line_color', (192, 0, 0)],
  172. ['res_style', 'color_table', 'byr'],
  173. ['res_style', 'invert_colors', False],
  174. ['point_symbol', 'point_size', 10],
  175. ['point_symbol', 'point_width', 2],
  176. ['point_colors', "unused", (131, 139, 139)],
  177. ['point_colors', "used1cat", (192, 0, 0)],
  178. ['point_colors', "used2cat", (0, 0, 255)],
  179. ['point_colors', "selected", (9, 249, 17)],
  180. ['other', "snap_tresh", 10],
  181. ['other', "max_hist_steps", 5]
  182. ]
  183. for init in initSettings:
  184. UserSettings.ReadSettingsFile()
  185. UserSettings.Append(dict=UserSettings.userSettings,
  186. group='vnet',
  187. key=init[0],
  188. subkey=init[1],
  189. value=init[2],
  190. overwrite=False)
  191. class VNETPointsData:
  192. def __init__(self, mapWin, an_data, an_params):
  193. self.mapWin = mapWin
  194. self.an_data = an_data
  195. self.an_params = an_params
  196. # information, whether mouse event handler is registered in map window
  197. self.handlerRegistered = False
  198. self.pointsChanged = Signal('VNETPointsData.pointsChanged')
  199. self.an_params.parametersChanged.connect(self.ParametersChanged)
  200. self.snapping = False
  201. self.data = []
  202. self.cols = {"name": ['use', 'type', 'topology', 'e', 'n'],
  203. "label": [_('use'), _('type'), _('topology'), 'e', 'n'],
  204. # TDO
  205. "type": [None, ["", _("Start point"), _("End Point")], None, float, float],
  206. "def_vals": [False, 0, "new point", 0, 0]
  207. }
  208. # registration graphics for drawing
  209. self.pointsToDraw = self.mapWin.RegisterGraphicsToDraw(
  210. graphicsType="point", setStatusFunc=self.SetPointStatus)
  211. self.SetPointDrawSettings()
  212. self.AddPoint()
  213. self.AddPoint()
  214. self.SetPointData(0, {'use': True, 'type': 1})
  215. self.SetPointData(1, {'use': True, 'type': 2})
  216. self.selected = 0
  217. def __del__(self):
  218. self.CleanUp()
  219. def CleanUp(self):
  220. self.mapWin.UnregisterGraphicsToDraw(self.pointsToDraw)
  221. if self.handlerRegistered:
  222. self.mapWin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN,
  223. self.OnMapClickHandler)
  224. def SetSnapping(self, activate):
  225. self.snapping = activate
  226. def GetSnapping(self):
  227. return self.snapping
  228. def AddPoint(self):
  229. self.pointsToDraw.AddItem(
  230. coords=(
  231. self.cols["def_vals"][3],
  232. self.cols["def_vals"][4]))
  233. self.data.append(self.cols["def_vals"][:])
  234. self.pointsChanged.emit(method="AddPoint", kwargs={})
  235. def DeletePoint(self, pt_id):
  236. item = self.pointsToDraw.GetItem(pt_id)
  237. if item:
  238. self.pointsToDraw.DeleteItem(item)
  239. self.data.pop(pt_id)
  240. self.pointsChanged.emit(method="DeletePoint", kwargs={"pt_id": pt_id})
  241. def SetPoints(self, pts_data):
  242. for item in self.pointsToDraw.GetAllItems():
  243. self.pointsToDraw.DeleteItem(item)
  244. self.data = []
  245. for pt_data in pts_data:
  246. pt_data_list = self._ptDataToList(pt_data)
  247. self.data.append(pt_data_list)
  248. self.pointsToDraw.AddItem(
  249. coords=(pt_data_list[3], pt_data_list[4]))
  250. self.pointsChanged.emit(
  251. method="SetPoints", kwargs={
  252. "pts_data": pts_data})
  253. def SetPointData(self, pt_id, data):
  254. for col, v in six.iteritems(data):
  255. if col == 'use':
  256. continue
  257. idx = self.cols["name"].index(col)
  258. self.data[pt_id][idx] = v
  259. # if type is changed checked columns must be recalculated by _usePoint
  260. if 'type' in data and 'use' not in data:
  261. data["use"] = self.GetPointData(pt_id)['use']
  262. if 'use' in data:
  263. if self._usePoint(pt_id, data["use"]) == -1:
  264. data["use"] = False
  265. idx = self.cols["name"].index("use")
  266. self.data[pt_id][idx] = data["use"]
  267. self.pointsChanged.emit(
  268. method="SetPointData", kwargs={
  269. "pt_id": pt_id, "data": data})
  270. def GetPointData(self, pt_id):
  271. return self._ptListDataToPtData(self.data[pt_id])
  272. def GetPointsCount(self):
  273. return len(self.data)
  274. def SetPointStatus(self, item, itemIndex):
  275. """Before point is drawn, decides properties of drawing style"""
  276. analysis, valid = self.an_params.GetParam("analysis")
  277. cats = self.an_data[analysis]["cmdParams"]["cats"]
  278. if itemIndex == self.selected:
  279. wxPen = "selected"
  280. elif not self.data[itemIndex][0]:
  281. wxPen = "unused"
  282. item.hide = False
  283. elif len(cats) > 1:
  284. idx = self.data[itemIndex][1]
  285. if idx == 2: # End/To/Sink point
  286. wxPen = "used2cat"
  287. else:
  288. wxPen = "used1cat"
  289. else:
  290. wxPen = "used1cat"
  291. item.SetPropertyVal('label', str(itemIndex + 1))
  292. item.SetPropertyVal('penName', wxPen)
  293. def SetSelected(self, pt_id):
  294. self.selected = pt_id
  295. self.pointsChanged.emit(method="SetSelected", kwargs={"pt_id": pt_id})
  296. def GetSelected(self):
  297. return self.selected
  298. def SetPointDrawSettings(self):
  299. """Set settings for drawing of points"""
  300. ptSize = int(
  301. UserSettings.Get(
  302. group='vnet',
  303. key='point_symbol',
  304. subkey='point_size'))
  305. self.pointsToDraw.SetPropertyVal("size", ptSize)
  306. colors = UserSettings.Get(group='vnet', key='point_colors')
  307. ptWidth = int(
  308. UserSettings.Get(
  309. group='vnet',
  310. key='point_symbol',
  311. subkey='point_width'))
  312. textProp = self.pointsToDraw.GetPropertyVal("text")
  313. textProp["font"].SetPointSize(ptSize + 2)
  314. for colKey, col in six.iteritems(colors):
  315. pen = self.pointsToDraw.GetPen(colKey)
  316. if pen:
  317. pen.SetColour(wx.Colour(col[0], col[1], col[2], 255))
  318. pen.SetWidth(ptWidth)
  319. else:
  320. self.pointsToDraw.AddPen(
  321. colKey,
  322. wx.Pen(
  323. colour=wx.Colour(
  324. col[0],
  325. col[1],
  326. col[2],
  327. 255),
  328. width=ptWidth))
  329. def ParametersChanged(self, method, kwargs):
  330. if "analysis" in list(kwargs["changed_params"].keys()):
  331. self._updateTypeCol()
  332. if self.an_params.GetParam("analysis")[0] == "v.net.path":
  333. self._vnetPathUpdateUsePoints(None)
  334. def _updateTypeCol(self):
  335. """Rename category values when module is changed. Expample: Start point -> Sink point"""
  336. colValues = [""]
  337. analysis, valid = self.an_params.GetParam("analysis")
  338. anParamsCats = self.an_data[analysis]["cmdParams"]["cats"]
  339. for ptCat in anParamsCats:
  340. colValues.append(ptCat[1])
  341. type_idx = self.cols["name"].index("type")
  342. self.cols['type'][type_idx] = colValues
  343. def _ptDataToList(self, pt_data):
  344. pt_list_data = [None] * len(self.cols['name'])
  345. for k, val in six.iteritems(pt_data):
  346. pt_list_data[self.cols["name"].index(k)] = val
  347. return pt_list_data
  348. def _ptListDataToPtData(self, pt_list_data):
  349. pt_data = {}
  350. for i, val in enumerate(pt_list_data):
  351. pt_data[self.cols["name"][i]] = val
  352. return pt_data
  353. def _usePoint(self, pt_id, use):
  354. """Item is checked/unchecked"""
  355. analysis, valid = self.an_params.GetParam("analysis")
  356. cats = self.an_data[analysis]["cmdParams"]["cats"]
  357. # TODO move
  358. # if self.updateMap:
  359. # up_map_evt = gUpdateMap(render = False, renderVector = False)
  360. # wx.PostEvent(self.dialog.mapWin, up_map_evt)
  361. if len(cats) <= 1:
  362. return 0
  363. use_idx = self.cols["name"].index("use")
  364. checkedVal = self.data[pt_id][1]
  365. # point without given type cannot be selected
  366. if checkedVal == 0:
  367. self.data[pt_id][use_idx] = False
  368. self.pointsChanged.emit(
  369. method="SetPointData", kwargs={
  370. "pt_id": pt_id, "data": {
  371. "use": False}})
  372. return -1
  373. if analysis == "v.net.path" and use:
  374. self._vnetPathUpdateUsePoints(pt_id)
  375. def _vnetPathUpdateUsePoints(self, checked_pt_id):
  376. alreadyChecked = []
  377. type_idx = self.cols["name"].index("type")
  378. use_idx = self.cols["name"].index("use")
  379. if checked_pt_id is not None:
  380. checkedKey = checked_pt_id
  381. alreadyChecked.append(self.data[checked_pt_id][type_idx])
  382. else:
  383. checkedKey = -1
  384. for iKey, dt in enumerate(self.data):
  385. pt_type = dt[type_idx]
  386. if ((pt_type in alreadyChecked and checkedKey != iKey)
  387. or pt_type == 0) and self.data[iKey][use_idx]:
  388. self.data[iKey][use_idx] = False
  389. self.pointsChanged.emit(
  390. method="SetPointData", kwargs={
  391. "pt_id": iKey, "data": {
  392. "use": False}})
  393. elif self.data[iKey][use_idx]:
  394. alreadyChecked.append(pt_type)
  395. def EditPointMode(self, activate):
  396. """Registers/unregisters mouse handler into map window"""
  397. if activate == self.handlerRegistered:
  398. return
  399. if activate:
  400. self.mapWin.RegisterMouseEventHandler(wx.EVT_LEFT_DOWN,
  401. self.OnMapClickHandler,
  402. 'cross')
  403. self.handlerRegistered = True
  404. else:
  405. self.mapWin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN,
  406. self.OnMapClickHandler)
  407. self.handlerRegistered = False
  408. self.pointsChanged.emit(
  409. method="EditMode", kwargs={
  410. "activated": activate})
  411. def IsEditPointModeActive(self):
  412. return self.handlerRegistered
  413. def OnMapClickHandler(self, event):
  414. """Take coordinates from map window"""
  415. # TODO update snapping after input change
  416. if event == 'unregistered':
  417. self.handlerRegistered = False
  418. return
  419. if not self.data:
  420. self.AddPoint()
  421. e, n = self.mapWin.GetLastEN()
  422. if self.snapping:
  423. # compute threshold
  424. snapTreshPix = int(UserSettings.Get(group='vnet',
  425. key='other',
  426. subkey='snap_tresh'))
  427. res = max(
  428. self.mapWin.Map.region['nsres'],
  429. self.mapWin.Map.region['ewres'])
  430. snapTreshDist = snapTreshPix * res
  431. params, err_params, flags = self.an_params.GetParams()
  432. vectMap = params["input"]
  433. if "input" in err_params:
  434. msg = _("new point")
  435. coords = SnapToNode(e, n, snapTreshDist, vectMap)
  436. if coords:
  437. e = coords[0]
  438. n = coords[1]
  439. msg = ("snapped to node")
  440. else:
  441. msg = _("new point")
  442. else:
  443. msg = _("new point")
  444. self.SetPointData(self.selected,
  445. {'topology': msg,
  446. 'e': e,
  447. 'n': n})
  448. self.pointsToDraw.GetItem(self.selected).SetCoords([e, n])
  449. if self.selected == len(self.data) - 1:
  450. self.SetSelected(0)
  451. else:
  452. self.SetSelected(self.GetSelected() + 1)
  453. def GetColumns(self, only_relevant=True):
  454. cols_data = deepcopy(self.cols)
  455. hidden_cols = []
  456. hidden_cols.append(self.cols["name"].index("e"))
  457. hidden_cols.append(self.cols["name"].index("n"))
  458. analysis, valid = self.an_params.GetParam("analysis")
  459. if only_relevant and len(self.an_data[analysis][
  460. "cmdParams"]["cats"]) <= 1:
  461. hidden_cols.append(self.cols["name"].index("type"))
  462. i_red = 0
  463. hidden_cols.sort()
  464. for idx in hidden_cols:
  465. for dt in six.itervalues(cols_data):
  466. dt.pop(idx - i_red)
  467. i_red += 1
  468. return cols_data
  469. class VNETAnalysisParameters:
  470. def __init__(self, an_props):
  471. self.an_props = an_props
  472. self.params = {"analysis": self.an_props.used_an[0],
  473. "input": "",
  474. "arc_layer": "",
  475. "node_layer": "",
  476. "arc_column": "",
  477. "arc_backward_column": "",
  478. "node_column": "",
  479. "turn_layer": "",
  480. "turn_cat_layer": "",
  481. "iso_lines": "", # TODO check validity
  482. "max_dist": 0} # TODO check validity
  483. self.flags = {"t": False}
  484. self.parametersChanged = Signal(
  485. 'VNETAnalysisParameters.parametersChanged')
  486. def SetParams(self, params, flags):
  487. changed_params = {}
  488. for p, v in six.iteritems(params):
  489. if p == "analysis" and v not in self.an_props.used_an:
  490. continue
  491. if p == "input":
  492. mapName, mapSet = ParseMapStr(v)
  493. v = mapName + "@" + mapSet
  494. if p in self.params:
  495. if isinstance(v, str):
  496. v = v.strip()
  497. self.params[p] = v
  498. changed_params[p] = v
  499. changed_flags = {}
  500. for p, v in six.iteritems(flags):
  501. if p in self.flags:
  502. self.flags[p] = v
  503. changed_flags[p] = v
  504. self.parametersChanged.emit(
  505. method="SetParams",
  506. kwargs={
  507. "changed_params": changed_params,
  508. "changed_flags": changed_flags})
  509. return changed_params, changed_flags
  510. def GetParam(self, param):
  511. invParams = []
  512. if param in [
  513. "input", "arc_layer", "node_layer", "arc_column",
  514. "arc_backward_column", "node_column", "turn_layer",
  515. "turn_cat_layer"]:
  516. invParams = self._getInvalidParams(self.params)
  517. if invParams:
  518. return self.params[param], False
  519. return self.params[param], True
  520. def GetParams(self):
  521. invParams = self._getInvalidParams(self.params)
  522. return self.params, invParams, self.flags
  523. def _getInvalidParams(self, params):
  524. """Check of analysis input data for invalid values (Parameters tab)"""
  525. # dict of invalid values {key from self.itemData (comboboxes from
  526. # Parameters tab) : invalid value}
  527. invParams = []
  528. # check vector map
  529. if params["input"]:
  530. mapName, mapSet = params["input"].split("@")
  531. if mapSet in grass.list_grouped('vector'):
  532. vectMaps = grass.list_grouped('vector')[mapSet]
  533. if not params["input"] or mapName not in vectMaps:
  534. invParams = params.keys()[:]
  535. return invParams
  536. # check arc/node layer
  537. layers = utils.GetVectorNumberOfLayers(params["input"])
  538. for l in ['arc_layer', 'node_layer', 'turn_layer', 'turn_cat_layer']:
  539. if not layers or params[l] not in layers:
  540. invParams.append(l)
  541. dbInfo = VectorDBInfo(params["input"])
  542. try:
  543. table = dbInfo.GetTable(int(params["arc_layer"]))
  544. columnchoices = dbInfo.GetTableDesc(table)
  545. except (KeyError, ValueError):
  546. table = None
  547. # check costs columns
  548. for col in ["arc_column", "arc_backward_column", "node_column"]:
  549. if col == "node_column":
  550. try:
  551. table = dbInfo.GetTable(int(params["node_layer"]))
  552. columnchoices = dbInfo.GetTableDesc(table)
  553. except (KeyError, ValueError):
  554. table = None
  555. if not table or not params[col] in list(columnchoices.keys()):
  556. invParams.append(col)
  557. continue
  558. if columnchoices[
  559. params[col]]['type'] not in [
  560. 'integer', 'double precision']:
  561. invParams.append(col)
  562. continue
  563. return invParams
  564. class VNETAnalysesProperties:
  565. def __init__(self):
  566. """Initializes parameters for different v.net.* modules """
  567. # initialization of v.net.* analysis parameters (data which
  568. # characterizes particular analysis)
  569. self.attrCols = {
  570. 'arc_column': {
  571. "label": _("Arc forward/both direction(s) cost column:"),
  572. "name": _("arc forward/both")
  573. },
  574. 'arc_backward_column': {
  575. "label": _("Arc backward direction cost column:"),
  576. "name": _("arc backward")
  577. },
  578. 'acolumn': {
  579. "label": _("Arcs' cost column (for both directions):"),
  580. "name": _("arc"),
  581. "inputField": 'arc_column',
  582. },
  583. 'node_column': {
  584. "label": _("Node cost column:"),
  585. "name": _("node")
  586. }
  587. }
  588. self.vnetProperties = {
  589. "v.net.path": {
  590. "label": _("Shortest path %s") % "(v.net.path)",
  591. "cmdParams": {
  592. "cats": [
  593. ["st_pt", _("Start point")],
  594. ["end_pt", _("End point")]
  595. ],
  596. "cols": [
  597. 'arc_column',
  598. 'arc_backward_column',
  599. 'node_column'
  600. ],
  601. },
  602. "resultProps": {
  603. "singleColor": None,
  604. "dbMgr": True # TODO delete this property, this information can be get from result
  605. },
  606. "turns_support": True
  607. },
  608. "v.net.salesman": {
  609. "label": _("Traveling salesman %s") % "(v.net.salesman)",
  610. "cmdParams": {
  611. "cats": [["center_cats", None]],
  612. "cols": [
  613. 'arc_column',
  614. 'arc_backward_column'
  615. ],
  616. },
  617. "resultProps": {
  618. "singleColor": None,
  619. "dbMgr": False
  620. },
  621. "turns_support": True
  622. },
  623. "v.net.flow": {
  624. "label": _("Maximum flow %s") % "(v.net.flow)",
  625. "cmdParams": {
  626. "cats": [
  627. ["source_cats", _("Source point")],
  628. ["sink_cats", _("Sink point")]
  629. ],
  630. "cols": [
  631. 'arc_column',
  632. 'arc_backward_column',
  633. 'node_column'
  634. ]
  635. },
  636. "resultProps": {
  637. "attrColColor": "flow",
  638. "dbMgr": True
  639. },
  640. "turns_support": False
  641. },
  642. "v.net.alloc": {
  643. "label": _("Subnets for nearest centers %s") % "(v.net.alloc)",
  644. "cmdParams": {
  645. "cats": [["center_cats", None]],
  646. "cols": [
  647. 'arc_column',
  648. 'arc_backward_column',
  649. 'node_column'
  650. ]
  651. },
  652. "resultProps": {
  653. "catColor": None,
  654. "dbMgr": False
  655. },
  656. "turns_support": True
  657. },
  658. "v.net.steiner": {
  659. "label": _("Steiner tree for the network and given terminals %s") % "(v.net.steiner)",
  660. "cmdParams": {
  661. "cats": [["terminal_cats", None]],
  662. "cols": [
  663. 'acolumn',
  664. ]
  665. },
  666. "resultProps": {
  667. "singleColor": None,
  668. "dbMgr": False
  669. },
  670. "turns_support": True
  671. },
  672. "v.net.distance": {
  673. "label": _("Shortest distance via the network %s") % "(v.net.distance)",
  674. "cmdParams": {
  675. "cats": [
  676. ["from_cats", "From point"],
  677. ["to_cats", "To point"]
  678. ],
  679. "cols": [
  680. 'arc_column',
  681. 'arc_backward_column',
  682. 'node_column'
  683. ],
  684. },
  685. "resultProps": {
  686. "catColor": None,
  687. "dbMgr": True
  688. },
  689. "turns_support": False
  690. },
  691. "v.net.iso": {
  692. "label": _("Cost isolines %s") % "(v.net.iso)",
  693. "cmdParams": {
  694. "cats": [["center_cats", None]],
  695. "cols": [
  696. 'arc_column',
  697. 'arc_backward_column',
  698. 'node_column'
  699. ]
  700. },
  701. "resultProps": {
  702. "catColor": None,
  703. "dbMgr": False
  704. },
  705. "turns_support": True
  706. }
  707. }
  708. self.used_an = ["v.net.path",
  709. "v.net.salesman",
  710. "v.net.flow",
  711. "v.net.alloc",
  712. "v.net.distance",
  713. "v.net.iso",
  714. #"v.net.steiner"
  715. ]
  716. for an in list(self.vnetProperties.keys()):
  717. if an not in self.used_an:
  718. del self.vnetProperties[an]
  719. continue
  720. cols = self.vnetProperties[an]["cmdParams"]["cols"]
  721. self.vnetProperties[an]["cmdParams"]["cols"] = {}
  722. for c in cols:
  723. self.vnetProperties[an]["cmdParams"][
  724. "cols"][c] = self.attrCols[c]
  725. def has_key(self, key):
  726. return key in self.vnetProperties
  727. def __getitem__(self, key):
  728. return self.vnetProperties[key]
  729. def GetRelevantParams(self, analysis):
  730. if analysis not in self.vnetProperties:
  731. return None
  732. relevant_params = ["input", "arc_layer", "node_layer"]
  733. if self.vnetProperties[analysis]["turns_support"]:
  734. relevant_params += ["turn_layer", "turn_cat_layer"]
  735. cols = self.vnetProperties[analysis]["cmdParams"]["cols"]
  736. for col, v in six.iteritems(cols):
  737. if "inputField" in col:
  738. colInptF = v["inputField"]
  739. else:
  740. colInptF = col
  741. relevant_params.append(colInptF)
  742. return relevant_params
  743. class VNETTmpVectMaps:
  744. """Class which creates, stores and destroys all tmp maps created during analysis"""
  745. def __init__(self, parent, mapWin):
  746. self.tmpMaps = [] # temporary maps
  747. self.parent = parent
  748. self.mapWin = mapWin
  749. def AddTmpVectMap(self, mapName, msg):
  750. """New temporary map
  751. :return: instance of VectMap representing temporary map
  752. """
  753. currMapSet = grass.gisenv()['MAPSET']
  754. tmpMap = grass.find_file(name=mapName,
  755. element='vector',
  756. mapset=currMapSet)
  757. fullName = tmpMap["fullname"]
  758. # map already exists
  759. if fullName:
  760. # TODO move dialog out of class, AddTmpVectMap(self, mapName,
  761. # overvrite = False)
  762. dlg = wx.MessageDialog(parent=self.parent,
  763. message=msg,
  764. caption=_("Overwrite map layer"),
  765. style=wx.YES_NO | wx.NO_DEFAULT |
  766. wx.ICON_QUESTION | wx.CENTRE)
  767. ret = dlg.ShowModal()
  768. dlg.Destroy()
  769. if ret == wx.ID_NO:
  770. return None
  771. else:
  772. fullName = mapName + "@" + currMapSet
  773. newVectMap = VectMap(self.mapWin, fullName)
  774. self.tmpMaps.append(newVectMap)
  775. return newVectMap
  776. def HasTmpVectMap(self, vectMapName):
  777. """
  778. :param: vectMapName name of vector map
  779. :return: True if it contains the map
  780. :return: False if not
  781. """
  782. mapValSpl = vectMapName.strip().split("@")
  783. if len(mapValSpl) > 1:
  784. mapSet = mapValSpl[1]
  785. else:
  786. mapSet = grass.gisenv()['MAPSET']
  787. mapName = mapValSpl[0]
  788. fullName = mapName + "@" + mapSet
  789. for vectTmpMap in self.tmpMaps:
  790. if vectTmpMap.GetVectMapName() == fullName:
  791. return True
  792. return False
  793. def GetTmpVectMap(self, vectMapName):
  794. """Get instance of VectMap with name vectMapName"""
  795. for vectMap in self.tmpMaps:
  796. if vectMap.GetVectMapName() == vectMapName.strip():
  797. return vectMap
  798. return None
  799. def RemoveFromTmpMaps(self, vectMap):
  800. """Temporary map is removed from the class instance however it is not deleted
  801. :param vectMap: instance of VectMap class to be removed
  802. :return: True if was removed
  803. :return: False if does not contain the map
  804. """
  805. try:
  806. self.tmpMaps.remove(vectMap)
  807. return True
  808. except ValueError:
  809. return False
  810. def DeleteTmpMap(self, vectMap):
  811. """Temporary map is removed from the class and it is deleted
  812. :param vectMap: instance of VectMap class to be deleted
  813. :return: True if was removed
  814. :return: False if does not contain the map
  815. """
  816. if vectMap:
  817. vectMap.DeleteRenderLayer()
  818. RunCommand('g.remove', flags='f', type='vector',
  819. name=vectMap.GetVectMapName())
  820. self.RemoveFromTmpMaps(vectMap)
  821. return True
  822. return False
  823. def DeleteAllTmpMaps(self):
  824. """Delete all temporary maps in the class"""
  825. update = False
  826. for tmpMap in self.tmpMaps:
  827. RunCommand('g.remove', flags='f', type='vector',
  828. name=tmpMap.GetVectMapName())
  829. if tmpMap.DeleteRenderLayer():
  830. update = True
  831. return update
  832. class VectMap:
  833. """Represents map
  834. It can check if it was modified or render it
  835. """
  836. def __init__(self, mapWin, fullName):
  837. self.fullName = fullName
  838. self.mapWin = mapWin
  839. self.renderLayer = None
  840. self.modifTime = None # time, for modification check
  841. def __del__(self):
  842. self.DeleteRenderLayer()
  843. def AddRenderLayer(self, cmd=None, colorsCmd=None):
  844. """Add map from map window layers to render """
  845. if not self.mapWin:
  846. return False
  847. existsMap = grass.find_file(name=self.fullName,
  848. element='vector',
  849. mapset=grass.gisenv()['MAPSET'])
  850. if not existsMap["name"]:
  851. self.DeleteRenderLayer()
  852. return False
  853. if not cmd:
  854. cmd = []
  855. cmd.insert(0, 'd.vect')
  856. cmd.append('map=%s' % self.fullName)
  857. if self.renderLayer:
  858. self.DeleteRenderLayer()
  859. if colorsCmd:
  860. colorsCmd.append('map=%s' % self.fullName)
  861. layerStyleVnetColors = cmdlist_to_tuple(colorsCmd)
  862. RunCommand(layerStyleVnetColors[0],
  863. **layerStyleVnetColors[1])
  864. self.renderLayer = self.mapWin.Map.AddLayer(
  865. ltype="vector", command=cmd, name=self.fullName, active=True,
  866. opacity=1.0, render=False, pos=-1)
  867. return True
  868. def DeleteRenderLayer(self):
  869. """Remove map from map window layers to render"""
  870. if not self.mapWin:
  871. return False
  872. if self.renderLayer:
  873. self.mapWin.Map.DeleteLayer(self.renderLayer)
  874. self.renderLayer = None
  875. return True
  876. return False
  877. def GetRenderLayer(self):
  878. return self.renderLayer
  879. def GetVectMapName(self):
  880. return self.fullName
  881. def SaveVectMapState(self):
  882. """Save modification time for vector map"""
  883. self.modifTime = self.GetLastModified()
  884. def VectMapState(self):
  885. """Checks if map was modified
  886. :return: -1 - if no modification time was saved
  887. :return: 0 - if map was modified
  888. :return: 1 - if map was not modified
  889. """
  890. if self.modifTime is None:
  891. return -1
  892. if self.modifTime != self.GetLastModified():
  893. return 0
  894. return 1
  895. def GetLastModified(self):
  896. """Get modification time
  897. :return: MAP DATE time string from vector map head file
  898. """
  899. mapValSpl = self.fullName.split("@")
  900. mapSet = mapValSpl[1]
  901. mapName = mapValSpl[0]
  902. headPath = os.path.join(grass.gisenv()['GISDBASE'],
  903. grass.gisenv()['LOCATION_NAME'],
  904. mapSet,
  905. "vector",
  906. mapName,
  907. "head")
  908. try:
  909. head = open(headPath, 'r')
  910. for line in head.readlines():
  911. i = line.find('MAP DATE:', )
  912. if i == 0:
  913. head.close()
  914. return line.split(':', 1)[1].strip()
  915. head.close()
  916. return ""
  917. except IOError:
  918. return ""
  919. class History:
  920. """Class which reads and saves history data (based on gui.core.settings Settings class file save/load)
  921. .. todo::
  922. Maybe it could be useful for other GRASS wxGUI tools.
  923. """
  924. def __init__(self):
  925. # max number of steps in history (zero based)
  926. self.maxHistSteps = 3
  927. # current history step
  928. self.currHistStep = 0
  929. # number of steps saved in history
  930. self.histStepsNum = 0
  931. # dict contains data saved in history for current history step
  932. self.currHistStepData = {}
  933. # buffer for data to be saved into history
  934. self.newHistStepData = {}
  935. self.histFile = grass.tempfile()
  936. # key/value separator
  937. self.sep = ';'
  938. def __del__(self):
  939. try_remove(self.histFile)
  940. def GetNext(self):
  941. """Go one step forward in history"""
  942. self.currHistStep -= 1
  943. self.currHistStepData.clear()
  944. self.currHistStepData = self._getHistStepData(self.currHistStep)
  945. return self.currHistStepData
  946. def GetPrev(self):
  947. """Go one step back in history"""
  948. self.currHistStep += 1
  949. self.currHistStepData.clear()
  950. self.currHistStepData = self._getHistStepData(self.currHistStep)
  951. return self.currHistStepData
  952. def GetStepsNum(self):
  953. """Get number of steps saved in history"""
  954. return self.histStepsNum
  955. def GetCurrHistStep(self):
  956. """Get current history step"""
  957. return self.currHistStep
  958. def Add(self, key, subkey, value):
  959. """Add new data into buffer"""
  960. if key not in self.newHistStepData:
  961. self.newHistStepData[key] = {}
  962. if isinstance(subkey, types.ListType):
  963. if subkey[0] not in self.newHistStepData[key]:
  964. self.newHistStepData[key][subkey[0]] = {}
  965. self.newHistStepData[key][subkey[0]][subkey[1]] = value
  966. else:
  967. self.newHistStepData[key][subkey] = value
  968. def SaveHistStep(self):
  969. """Create new history step with data in buffer"""
  970. self.maxHistSteps = UserSettings.Get(group='vnet',
  971. key='other',
  972. subkey='max_hist_steps')
  973. self.currHistStep = 0
  974. newHistFile = grass.tempfile()
  975. newHist = open(newHistFile, "w")
  976. self._saveNewHistStep(newHist)
  977. oldHist = open(self.histFile)
  978. removedHistData = self._savePreviousHist(newHist, oldHist)
  979. oldHist.close()
  980. newHist.close()
  981. try_remove(self.histFile)
  982. self.histFile = newHistFile
  983. self.newHistStepData.clear()
  984. return removedHistData
  985. def _savePreviousHist(self, newHist, oldHist):
  986. """Save previous history into new file"""
  987. newHistStep = False
  988. removedHistData = {}
  989. newHistStepsNum = self.histStepsNum
  990. for line in oldHist.readlines():
  991. if not line.strip():
  992. newHistStep = True
  993. newHistStepsNum += 1
  994. continue
  995. if newHistStep:
  996. newHistStep = False
  997. line = line.split("=")
  998. line[1] = str(newHistStepsNum)
  999. line = "=".join(line)
  1000. if newHistStepsNum >= self.maxHistSteps:
  1001. removedHistStep = removedHistData[line] = {}
  1002. continue
  1003. else:
  1004. newHist.write('%s%s%s' % (os.linesep, line, os.linesep))
  1005. self.histStepsNum = newHistStepsNum
  1006. else:
  1007. if newHistStepsNum >= self.maxHistSteps:
  1008. self._parseLine(line, removedHistStep)
  1009. else:
  1010. newHist.write('%s' % line)
  1011. return removedHistData
  1012. def _saveNewHistStep(self, newHist):
  1013. """Save buffer (new step) data into file"""
  1014. newHist.write('%s%s%s' % (os.linesep, "history step=0", os.linesep))
  1015. for key in list(self.newHistStepData.keys()):
  1016. subkeys = list(self.newHistStepData[key].keys())
  1017. newHist.write('%s%s' % (key, self.sep))
  1018. for idx in range(len(subkeys)):
  1019. value = self.newHistStepData[key][subkeys[idx]]
  1020. if isinstance(value, types.DictType):
  1021. if idx > 0:
  1022. newHist.write('%s%s%s' % (os.linesep, key, self.sep))
  1023. newHist.write('%s%s' % (subkeys[idx], self.sep))
  1024. kvalues = list(self.newHistStepData[key][subkeys[idx]].keys())
  1025. srange = range(len(kvalues))
  1026. for sidx in srange:
  1027. svalue = self._parseValue(
  1028. self.newHistStepData[key][
  1029. subkeys[idx]][
  1030. kvalues[sidx]])
  1031. newHist.write(
  1032. '%s%s%s' %
  1033. (kvalues[sidx], self.sep, svalue))
  1034. if sidx < len(kvalues) - 1:
  1035. newHist.write('%s' % self.sep)
  1036. else:
  1037. if idx > 0 and isinstance(
  1038. self.newHistStepData[key][subkeys[idx - 1]],
  1039. types.DictType):
  1040. newHist.write('%s%s%s' % (os.linesep, key, self.sep))
  1041. value = self._parseValue(
  1042. self.newHistStepData[key][subkeys[idx]])
  1043. newHist.write('%s%s%s' % (subkeys[idx], self.sep, value))
  1044. if idx < len(subkeys) - 1 and not isinstance(
  1045. self.newHistStepData[key][subkeys[idx + 1]],
  1046. types.DictType):
  1047. newHist.write('%s' % self.sep)
  1048. newHist.write(os.linesep)
  1049. self.histStepsNum = 0
  1050. def _parseValue(self, value, read=False):
  1051. """Parse value"""
  1052. if read: # -> read data (cast values)
  1053. if value:
  1054. if value[
  1055. 0] == '[' and value[-1] == ']': # TODO, possible wrong interpretation
  1056. value = value[1:-1].split(',')
  1057. value = map(self._castValue, value)
  1058. return value
  1059. if value == 'True':
  1060. value = True
  1061. elif value == 'False':
  1062. value = False
  1063. elif value == 'None':
  1064. value = None
  1065. elif ':' in value: # -> color
  1066. try:
  1067. value = tuple(map(int, value.split(':')))
  1068. except ValueError: # -> string
  1069. pass
  1070. else:
  1071. try:
  1072. value = int(value)
  1073. except ValueError:
  1074. try:
  1075. value = float(value)
  1076. except ValueError:
  1077. pass
  1078. else: # -> write data
  1079. if isinstance(value, type(())): # -> color
  1080. value = str(value[0]) + ':' +\
  1081. str(value[1]) + ':' + \
  1082. str(value[2])
  1083. return value
  1084. def _castValue(self, value):
  1085. """Cast value"""
  1086. try:
  1087. value = int(value)
  1088. except ValueError:
  1089. try:
  1090. value = float(value)
  1091. except ValueError:
  1092. value = value[1:-1]
  1093. return value
  1094. def _getHistStepData(self, histStep):
  1095. """Load data saved in history step"""
  1096. hist = open(self.histFile)
  1097. histStepData = {}
  1098. newHistStep = False
  1099. isSearchedHistStep = False
  1100. for line in hist.readlines():
  1101. if not line.strip() and isSearchedHistStep:
  1102. break
  1103. elif not line.strip():
  1104. newHistStep = True
  1105. continue
  1106. elif isSearchedHistStep:
  1107. self._parseLine(line, histStepData)
  1108. if newHistStep:
  1109. line = line.split("=")
  1110. if int(line[1]) == histStep:
  1111. isSearchedHistStep = True
  1112. newHistStep = False
  1113. hist.close()
  1114. return histStepData
  1115. def _parseLine(self, line, histStepData):
  1116. """Parse line in file with history"""
  1117. line = line.rstrip('%s' % os.linesep).split(self.sep)
  1118. key = line[0]
  1119. kv = line[1:]
  1120. idx = 0
  1121. subkeyMaster = None
  1122. if len(kv) % 2 != 0: # multiple (e.g. nviz)
  1123. subkeyMaster = kv[0]
  1124. del kv[0]
  1125. idx = 0
  1126. while idx < len(kv):
  1127. if subkeyMaster:
  1128. subkey = [subkeyMaster, kv[idx]]
  1129. else:
  1130. subkey = kv[idx]
  1131. value = kv[idx + 1]
  1132. value = self._parseValue(value, read=True)
  1133. if key not in histStepData:
  1134. histStepData[key] = {}
  1135. if isinstance(subkey, types.ListType):
  1136. if subkey[0] not in histStepData[key]:
  1137. histStepData[key][subkey[0]] = {}
  1138. histStepData[key][subkey[0]][subkey[1]] = value
  1139. else:
  1140. histStepData[key][subkey] = value
  1141. idx += 2
  1142. def DeleteNewHistStepData(self):
  1143. """Delete buffer data for new history step"""
  1144. self.newHistStepData.clear()
  1145. class VNETGlobalTurnsData:
  1146. """Turn Data"""
  1147. def __init__(self):
  1148. # Definition of four basic directions
  1149. self.turn_data = [
  1150. ["Straight", DegreesToRadians(-30), DegreesToRadians(+30), 0.0],
  1151. ["Right Turn", DegreesToRadians(+30), DegreesToRadians(+150), 0.0],
  1152. ["Reverse", DegreesToRadians(+150), DegreesToRadians(-150), 0.0],
  1153. ["Left Turn", DegreesToRadians(-150), DegreesToRadians(-30), 0.0]
  1154. ]
  1155. def GetData(self):
  1156. data = []
  1157. for ival in self.turn_data:
  1158. data.append(ival[1:])
  1159. return data
  1160. def GetValue(self, line, col):
  1161. return self.turn_data[line][col]
  1162. def GetLinesCount(self):
  1163. return len(self.turn_data)
  1164. def SetValue(self, value, line, col):
  1165. self.DataValidator(line, col, value)
  1166. self.turn_data[line][col] = value
  1167. def SetUTurns(self, value):
  1168. """Checked if checeBox is checed"""
  1169. useUTurns = value
  1170. def AppendRow(self, values):
  1171. self.turn_data.append(values)
  1172. def InsertRow(self, line, values):
  1173. self.turn_data.insert(line, values)
  1174. def PopRow(self, values):
  1175. self.RemoveDataValidator(values)
  1176. self.turn_data.pop(values)
  1177. def DataValidator(self, row, col, value):
  1178. """Angle recalculation due to value changing"""
  1179. if col not in [1, 2]:
  1180. return
  1181. if col == 1:
  1182. new_from_angle = value
  1183. old_from_angle = self.turn_data[row][1]
  1184. new_to_angle = self.turn_data[row][2]
  1185. if self.IsInInterval(old_from_angle, new_to_angle, new_from_angle):
  1186. prev_row = row - 1
  1187. if prev_row == -1:
  1188. prev_row = len(self.turn_data) - 1
  1189. self.turn_data[prev_row][2] = new_from_angle
  1190. return
  1191. if col == 2:
  1192. new_to_angle = value
  1193. old_to_angle = self.turn_data[row][2]
  1194. new_from_angle = self.turn_data[row][1]
  1195. if self.IsInInterval(new_from_angle, old_to_angle, new_to_angle):
  1196. next_row = row + 1
  1197. if len(self.turn_data) == next_row:
  1198. next_row = 0
  1199. self.turn_data[next_row][1] = new_to_angle
  1200. return
  1201. inside_new = []
  1202. overlap_new_from = []
  1203. overlap_new_to = []
  1204. for i in range(self.GetLinesCount()):
  1205. if i == row:
  1206. continue
  1207. from_angle = self.turn_data[i][1]
  1208. is_in_from = self.IsInInterval(
  1209. new_from_angle, new_to_angle, from_angle)
  1210. to_angle = self.turn_data[i][2]
  1211. is_in_to = self.IsInInterval(
  1212. new_from_angle, new_to_angle, to_angle)
  1213. if is_in_from and is_in_to:
  1214. inside_new.append(i)
  1215. if is_in_from:
  1216. overlap_new_to.append(i)
  1217. if is_in_to:
  1218. overlap_new_from.append(i)
  1219. for i_row in overlap_new_from:
  1220. self.turn_data[i_row][2] = new_from_angle
  1221. for i_row in overlap_new_to:
  1222. self.turn_data[i_row][1] = new_to_angle
  1223. for i_row in inside_new:
  1224. if col == 1:
  1225. angle = new_from_angle
  1226. else:
  1227. angle = new_to_angle
  1228. self.turn_data[i_row][1] = angle
  1229. self.turn_data[i_row][2] = angle
  1230. def RemoveDataValidator(self, row):
  1231. """Angle recalculation due to direction remove"""
  1232. if row == 0:
  1233. prev_row = self.GetLinesCount() - 1
  1234. else:
  1235. prev_row = row - 1
  1236. remove_to_angle = self.turn_data[row][2]
  1237. self.turn_data[prev_row][2] = remove_to_angle
  1238. def IsInInterval(self, from_angle, to_angle, angle):
  1239. """Test if a direction includes or not includes a value"""
  1240. if to_angle < from_angle:
  1241. to_angle = math.pi * 2 + to_angle
  1242. if angle < from_angle:
  1243. angle = math.pi * 2 + angle
  1244. if angle > from_angle and angle < to_angle:
  1245. return True
  1246. return False