vnet_data.py 56 KB

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